这里讲的分段指的保护模式(protected mode)下的段机制。与保护模式相对的一个概念是实模式(real mode),实模式的地址由“段基址:偏移量”组成,段基址是个16位的寄存器(CS,DS等),这样显然限制了系统的寻址空间。保护模式的地址由“段选择子+偏移量组成”,段选择子(Segment Selector)仍然由16位寄存器(CS,DS等)指定,但是其意义也不再是段基址,而是指定了一个下标,用于从描述符表(X86把所有的段信息都存在这个表中)中提取对应的段信息。
段选择子格式:高12位作为下标从段描述符表选择对应段信息,第2位指示从哪个描述符表寻找(TI=0是取GDT),在X86中共有两个,一个是全局描述符表GDT(Global Descriptor Table),另一个是局部描述符表LDT(Local Descriptor Table),低2位用于指示优先级RPL(Request Privilege Level)。X86共有4中优先级(也叫4个ring),不过linux仅仅使用0和3。
段描述符格式:段描述符中的BASE(32位)指定了该段的基址,LIMIT(26位)指定了段的范围,具体数值要根据G Bit来看,因为给定的是一个“粒度”,相当于一个单位,一般情况下是4K,所以总共就是2^26 * 4K = 2 ^ 32。另外TYPE指明该段的属性,比如是否代码、可执行等,而DPL是与优先级相关的属性。
分段机制(逻辑地址 -- 线性地址)
Linux对分段机制的应用
Linux对段机制的应用效果是等价于几乎绕过了段基址。在Linux中仅有4个段,用户代码段、数据段和内核代码段、数据段。
Segment
Base
G
Limit
S
Type
DPL
D/B
P
user code
0x00000000
1
0xfffff
1
10
3
1
1
user data
0x00000000
1
0xfffff
1
2
3
1
1
kernel code
0x00000000
1
0xfffff
1
10
0
1
1
kernel data
0x00000000
1
0xfffff
1
2
0
1
1
不难看到,由于Base都为0,经分段机制后“线性地址=逻辑地址”,并且G=1(4K粒度)Limit=2^20,因此最大寻址空间为2^32。用户层与内核层描述符之间的区别在于DPL。
上述四个描述符都在统一放在GDT中,事实上Linux对LDT也是没怎么使用。GDT中内容如下: