“其触牙者,即言象形如芦菔根;其触耳者,言象如箕;其触头者,言象如石;其触鼻者,言象如杆;其触脚者,言象如木臼;其触背者,言象如床;其触腹者,言象如瓮;其触尾者,言象如绳。”
我不得不承认的一点是:郑老师诙谐通俗的语言很大程度上降低了学习的难度。你可能会认为语言本质上只是表述的方式,只要实质含义相同,那么两段不同的话理解起来应是一致的。但事实上语言是最可控的内容传输的门槛,儿童只能读懂绘本里的“婴语”,而你我可以读懂专业论文或是文言文。
不知大家是否和我一样,对操作系统最直接的疑问产生在软硬件的互通上,而非一些调度的算法、文件管理之类。操作系统不可能总是及时更新各种硬件驱动,因而有了各种硬件适配设备,即IO接口(标准),以实现通用。书中提及的访问外部硬件方式有二:将外设的内存映射到一定范围的地址空间中(如显卡);通过IO口与CPU通信。访问IO接口依靠的是IO接口上的寄存器,即端口。郑老师一直强调所有设备都是“各干各的”,充分信任自己的同事与上下级,再按照规定把自己的事情做好,仅此而已。这一“分工”的概念非常关键,日后还会多次出现。
关于内存访问为什么分段,这里便提到了历史遗留问题:多数操作系统需要向下兼容,即保留旧版本的部分内容和模式。早期访问存储单元需直接给出绝对物理地址,程序想要在硬件上运行无误就必须如此,当然也包括操作系统和编译器。那时硬件太贵,它是老大,都得听它的。
但若程序中的地址都是绝对物理地址,那该程序就必须放在内存固定的地方,这导致两个编译出来地址相同的用户程序没法同时运行,效率很低。这直接催生了“分段”这一解决方法,即让CPU采用“段基址+段内偏移地址”的方式访问内存,能让程序重定位以实现程序并行。如此的分段访问形式需要专门提供段基址寄存器,实现这一步的“地址计算”功能,但好在这不是大问题。总而言之,把程序分了段,整个段不管丢到哪个位置,只要找到段基址和段内偏移地址,CPU就能找到你家。
程序分段“首先”是为了重定位,其次呢?早期寄存器仅16位,而最小的内存也有1MB,即20位,如何让16位的寄存器访问20位地址空间呢?如16位bx寄存器,最大可表示0~0xFFFF共64KB,即便段基址和段内偏移地址都拉满,也仅能达到17位。而理论上虽然能通过编程技巧(将偏移地址直接写成20位立即数)解决,我们最好还是了解一下CPU大人的想法:它在地址处理单元中动手脚,直接在接收到的段基址乘以16(左移4位)再和偏移地址相加,于是20位的地址就水灵灵地诞生了。
关于“段”,郑老师还专门阐述了编译器编译的段(程序分段)与内存访问中的段(内存分段)的区别。
- 程序分段:是程序组织和逻辑上的概念。它告诉链接器和操作系统,“这部分是代码”,“那部分是数据”。
- 内存分段:是内存管理和保护的机制。它是CPU和操作系统用来隔离不同用途的内存块(如代码区、数据区),并实施保护(防止代码被意外写入,防止数据被当作代码执行)的技术。
二者的关系是:编译器和链接器输出的“程序段”,为操作系统和CPU设置“内存段”提供了逻辑蓝图和初始化数据。
现代操作系统都是在平坦模型下工作,4GB为一个段,编译器也按照平坦模型为程序布局,所以程序中的代码和数据都在同一个段中整齐排列。我们运用高级语言开发程序时不关心这些,当然也不归我们管。
那么回到我们的标题——郑老师说到,很多人对操作系统的了解就如盲人摸象,能窥探其一其二,摸到鼻子觉得长,摸到大腿觉得粗。我们希望能够认识操作系统的全貌,而并非单纯陷入其中的细枝末节,这需要更高维的视角与足够强大的思维整理能力。技术细节十分重要,而其中渗透的思想更为关键。
第0章的典型问题属实过多了,我们先只讲述此部分,让可怜的小脑瓜冷却一会儿。