谈谈分段机制
在Lab2的笔记中,比较详细地描述了内存管理中的分页机制,但是并没有讲分段机制。这篇文章试着讲讲。
1 |
|
这是整个地址的转换过程。
从实模式讲起
在实模式中,需要寻址20位的空间,2^20即1MB。但4个相关的寄存器CS(code segment)
, DS(data segment)
, SS(stack segment)
, ES(extra segment)
都是16位的。寻址时用到16位的CS和一个16位的IP(Instruction Pointer)
:把16位的CS
左移4位,低4位就全为0,再加上16位的IP
,得到20位的地址,即为物理地址。这样也被称为CS:IP
对。
注意:每个segment都是16位的,2^16(64KB)。但每段并不一定用完这64KB。而且不同的CS:IP
对也可能计算得出相同的物理地址,所以在内存空间中,不同的segment可能会有重叠。
保护模式
80286中的保护模式
从80286中,有了保护模式,现在可寻址24位的空间,2^24(16MB)。但并不是通过继续左移CS
来完成的。现在的CS
里有一个index
,指向一个segment descriptor
,在这个descriptor
中有一个24位的基地址,用16位的IP
加上这个基地址就得到了物理地址。
80386中的保护模式
80386在分段后多了一个分页机制,还多了两个段寄存器FS
和GS
,CPU没有指定这两个寄存器的用途。下面是80386的一些寄存器。
主要看segment register
,每个寄存器都是16位,但是还有不可见部分。
在可见部分的16位,结构如下:
每个16位中都有13位的index
,最多可指向2^13(8192)个descriptor
。Table Indicator
指示是GDT还是LDT。RPL是保护模式中的等级。
GDT(global descriptor table)就像一个数组,每项为64位(8byte),最多可以有8192项,每项的结构为:
有32位的基地址,segment limit
的长度为20位。其他的是一些权限、控制位。
保护模式的流程
在80386中,有GDT和LDT。通过SGDT
(store)得到GDTR(GDT Register)
,GDTR
里存储了GDT的基地址和GDT的大小。LGDT
(load)存入GDTR
。LDT同理。寻址时,通过DS/CS
等segment register
中的index
找到对应的index,乘以8后加上GDTR
中的基地址,即可找到对应的项(index*8+基地址,*8是因为segment descriptor
的大小为8byte)。在segment descriptor
中得到对应的segment
的基地址后,加上32位的EIP
,形成线性地址,下一步就是分页。
通过这种方式,使得GDT和LDT存储了一些全局的和局部的descriptor
,程序运行时,通过对应的segment register
找到这些descriptor
,这样就形成了分段。通过不同的段进行第一层的内存管理。
扁平模式
然而,后来的操作系统并不想使用这个从16位系统遗留下来的分段机制。但是CPU为了兼容性还保留了分段机制,而且不能关闭,所以许多操作系统模拟扁平模式。具体做法是让kernel CS/kernel DS/user CS/user DS
对应的segment descriptor
基地址都为0,limit
都为4GB,这样就使得通过分段后,得到的线性地址为0+EIP
还是等于EIP
,分段对EIP
无影响。而EIP
是32位的,已经能独立寻址4GB的空间,也是在limit
的范围之内的。通过这种方式,在不关闭分段机制的前提下使得地址转换不受分段机制的影响。
有一个问题是limit
位只有20位,如何能表示32位(4GB)的空间?这是因为在segment descriptor
中有一个Granularity
位,为0时表示limit
的单位为byte,此时最多包括2^20(1MB)的空间;为1时表示limit
的单位为4096byte(一页),此时最多包括2^20(1M)*4KB=4GB的空间,和之前的是相对应的。