11.linux的分段机制与分页机制
1段式管理.
32位CPU两种工作方式:由实模式和保护模式组成。
1、 实模式:内存管理与 16位CPU是一致的。
2、 保护模式:(一般X86运行模式)
段基地址长达32位,每个段的最大容量可达4G,段寄存器的值时段地址的“选择器”(selecor),用该“选择器”从内存中得到一个32位的段地址,存储单元的
线性地址=该段基地址(Base)+段内偏移地址(offset),Linux对分段使用非常有限。作为一个跨硬件体系的操作系统,要支持多种硬件体系,而一些硬件体系结构式不支持分段的,Linux把所有段起始地址都设为0。所以逻辑地址=虚拟地址
一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节,如图
索引号就是“段描述符(segment descriptor)”,段描述符具体描述了一个段。这样,很多个段描述符,就组了一个数组,叫“段描述符表”,这样,可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段,每一个段描述符由8个字节组成。
段描述符
<dl> <dt> 每个段的属性在内存中由一个 8 字节的段描述符表示,段描述符存放在全局描述符表( GDT )或者局部描述符表( LDT )中,其地址和体积分别保存在 GDTR 和 LDTR 寄存器中。段描述符的结构如下: </dt> </dl>
Base24-Base31 | G | D/B | L | AVL | Limit16-Limit19 | |||
P | DPL | S | Type | Base16-Base23 | ||||
Base0-Base15 | ||||||||
Limit0-Limit15 |
在linux中,所有用户进程都使用相同的代码段(__USER_CS)和数据段(__USER_DS),所有的内核进程都使用相同的代码段(__KERNEL_CS)和数据段(__KERNEL_DS)。这四个段的段描述符主要字段如下:
Segment | Base | G | Limit | S | Type | DPL | D/B | P |
__USER_CS | 0x00000000 | 1 | 0xFFFFF | 1 | 10 | 3 | 1 | 1 |
__USER_DS | 0x00000000 | 1 | 0xFFFFF | 1 | 2 | 3 | 1 | 1 |
__KERNEL_CS | 0x00000000 | 1 | 0xFFFFF | 1 | 10 | 0 | 1 | 1 |
__KERNEL_DS | 0x00000000 | 1 | 0xFFFFF | 1 | 2 | 0 | 1 | 1 |
严格说Linux采用段页式内存管理,也就是既分段,又分页。地址映射的时候,先确定对应的段,确定段基地址;段内分页,再找到对应的页表项,确定页基地址;再由逻辑地址低位确定的页偏移量,就能找到最终的物理地址。但是,实际上Linux采用的是页式内存管理。原因是Linux中的段基地址都是0,相当于所有的段都是相同的。这样做的原因是某些体系结构的硬件限制,比如Intel的i386。作为软件的操作系统,必须要符合硬件体系。虽然所有段基地址都是0,但是段的概念在Linux内核中是确实存在的。比如常见的内核代码段、内核数据段、用户态代码段、用户态数据段等。除了符合硬件要求外,段也是有实际意义的。
逻辑地址分为两部分组成:段标识符和指定段内相对地址的偏移量。
段描述符表:存放段描述的表项。
段寄存器:存放段标识符。6个段寄存器称为cs(代码段寄存器),ss(栈段寄存器),ds(数据段寄存器),es,fs 和gs。
段基地址寄存器:指向段描述符表地址。
2.2页式管理(分页管理)
1、 线性地址页:从管理和效率的角度出发,线性地址被分为固定长度的组,称为页(page)。例如32位机器,线性地址最大可为4G,如果用4KB为页容量,这样将线性地址划分为2^20个页。
2、 物理页:另一类“页”,称为“物理页”,或者是“页框、页帧”。分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与线性地址页是相同。
如何将两者之间的映射?通过页式管理实现映射。
页式管理具体流程:
1、 分页单元中,页目录的地址放在CPU的CR3寄存器中,是进行地址转换的起始点。
2、 每个进程,都有其独立的虚拟地址空间,运行一个进程,首先需要将它的页目录地址放到CR3寄存器中,将其他进程保存下来。
3、 每一个32位的线性地址被划分三部分:页目录索引(10位):页表索引(10位):偏移(12位)
下面是地址转换的步骤:
第一步:装入进程的页目录地址(操作系统在调度进程时,把这个地址装入CR3)
第二步:根据线性地址前十位,在页目录中,找到对应的索引项 即页表地址。
第三步:根据线性地址中间十位,在页表中,找到对应的索引项 即页的起始地址。
第四步:将页的起始地址与线性地址最后12位相加,等到物理地址。
在二级模式下,页容量由线性地址[bit11:0]决定,页容量=2^12=4Kbyte。