1.1 简介
操作系统的内核和操作系统所管理的进程之间的关系就是一个clinet/server的关系,进程发出请求,内核相应请求并服务。但其实一些内核提供的功能或者服务,可以以进程的方式独立于内核之外,这种设计思想被称为微内核。就是尽可能的让内核小一些,把不必要的代码拆分出去。与之相对的是宏内核,我们的LInux是一种宏内核。
微内核的优点是体积小,可以在一些嵌入式的环境上运行,但是一些常用的系统服务被设计成进程的模式,那么用户进程在请求系统服务的时候,就会频繁的涉及到进程通信和进程切换,代价较大。
1.2 Intel x86 CPU系列CPU的寻址方式
这么底层的硬件知识让我想到了大三学微机处理的日子。
沿着往日的时光从8086回忆起。8086是一个16为的CPU,CPU的位数指的是CPU的算数逻辑单元(ALU)的位数,即一个CPU可以同时处理的字节的位数。除此之外还要搞清楚位数和总线宽度的关系。一般而言数据总线的宽度是和ALU的位数相等的,这样以来数据总线上的数据直接传到ALU上就可以参与运算,但其实也有例外,但最起码8086 80386是这样的。
地址总线的位数设计就是一个老大难的问题了,当然可以把地址总线的数目设计成和ALU位数相等,但是这样一个CPU的寻址能力就受限于其位数,一个16位的CPU只能寻址64K的空间。Intel的设计师给出的答案是把逻辑地址分成两部分:段基址和偏移地址,其中段基址和偏移地址都是16位的,然后在使用的时候段基址左移4位加上偏移地址就是物理地址。最终的物理地址是20位的,1M寻址空间。但其实当年Intel的设计路程,他们是先决定采用1M的存储空间即20位的地址总线,然后才考虑如何把16为的地址数据转换成20位的数据。当时可以选择的转换方式有很多,他们选择了段基址和偏移地址结合的策略,也就有了后来操作系统里的分段机制。
这么设计有什么问题呢?问题在于整个内存空间对所有进程都是可以访问的:进程可以随意访问一个段内的64K的所有空间,除此之外改变段基址的指令也不是特权指令,进程可以随意访问所有段。这就催生了后来的80386的实模式和保护模式。所谓保护模式保护的是内存,不让任意进程都可以随意访问任意内存空间。
80386是一个32位的CPU,即ALU是32位的,而且其数据总线、地址总线也是32位的。CPU内部寄存器呢?如果可以的话肯定设计成32位的最简单,但是为了和前面的CPU兼容其内部寄存器被设计成16位的。现在最难的问题是如何利用16位的寄存器生成32位的地址,并且实现对内存访问的保护即保护模式。
80386的逻辑地址表示的形式也是段寄存器:偏移地址的形式,并且段寄存器也是16位的。在保护模式下16的寄存器如CS寄存器的内容不再左移相应的位数并和段内偏移相加,该16位地址的13位被当做一个索引,指向某个段描述符的集合中的某个段描述符,其中段描述符就包括段基址和段的一些控制信息。在试图访问一个段的时候要先根据该段的控制信息判断该次内存访问是否合法,从而实现了保护模式。
首先根据指令的性质选择需要操作的寄存器。然后根据段寄存器里的内容找到合适的段描述符。段描述符结构如下:
根据TI标志选择GDT或者LDT然后从gdtr或者ldtr寄存器选择段基址,然后把段基址和高13的索引组合起来选择合适的段描述符。
段描述符是8位64字节的一个特殊的数据结构,其中包含段基址、段长度、段描述符等内容,段描述符包含下述内容。
1.2 i386的页式内存管理机制
在80386里,逻辑地址通过段映射后变成一个线性地址,这个地址并不是最终的物理地址,虽然线性地址的长度和最终的物理地址长度都是32位。这样的好处是为了避免内存碎片,提高内存的可用度。
线性地址空间里每4K被划分为一个页,每一个页需要再物理地址空间里有一个唯一对应的4K的帧,虽然整体的线性地址空间未必是连续的,但是一个页内4K大小的地址空间映射到物理地址上一定是连续的。
所以每拿到一个32位的线性地址都要把他转换成一个32位的物理地址。
32位的线性地址被分为三个部分,分别用于定位页面目录,定位页面表,定位该线性地址对应的页的真实物理地址是多少。通过三个级别的索引,把32位的线性地址映射成一个32位的物理地址。
对着这个图描述一下一个线性地址转换成物理地址的过程。首先从CR3中取出目录的基址,用线性地址的dir部分去定位页面表的基址,然后拿着线性地址的页面表部分和页面表基址去定位页面描述项,然后页内偏移和页面描述项组合起来定位一个页。
目录项和页表项的每一个元素所包含的内容相似,除了包含地址之外还有一些控制信息,其中最重要的一位的最低位p,代表该目录项or页表项所管理的内存上所存储的数据是否在内存里,这一大串真拗口。可以将内存中暂时不没使用到的页面写入磁盘的交换区,然后把相应的页表项的p置为0,代表当前页表不在内存里。所以当试图存取一个p为0的页表项或者目录项的时候,会引发缺页异常,异常处理程序会把不在内存里的目录或者页表从磁盘置换回来。