段寄存器

均为16位

寄存器功能

CS(code segment)代码段地址寄存器,存放代码段的起始地址

DS(data segment)数据段地址寄存器,存放数据段的起始地址

SS(stack segment)堆栈段地址寄存器,存放堆栈段的起始地址

ES(extra segment)附加段地址寄存器,存放附加段的起始地址

段选择符

实模式下的段寄存器(如cs,ss等)在保护模式下叫段选择子(Selector),用来存放段选择符

8c230df00498eaa03cc9f5bf7bf3dcf4.png

由于段选择子就是段寄存器是16位的,段选择符是16位的

2a143ddf7d043ed4d72ec5e55e770640.png

段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符。然后用描述符表中的段基址加上逻辑地址(SEL:OFFSET)的OFFSET就可以转换成线性地址,段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。

index13位 TI1位 RPL2位

index索引用于寻找GDT或LDT中的段描述符的位置,所以能够保存在GDT中的段描述符数最大为2^13-1=8191

Linux的特权级只使用了0和2

例如给出逻辑地址:21h:12345678h转换为线性地址

a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1

b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h

原文链接:https://blog.csdn.net/billpig/article/details/5833980

段描述符

段描述符8个字节(64位),存放在DGT或者LDT中,内部结构如下图

983a9fb70ee44b18b12182778527f03c.png

656b9fbd71dc3b4085f0661b7cabb5f7.png

段式管理单元

61d7a88b53a8e72d5db53384422e277d.png

程序过来一个逻辑地址,使用其段标识符(也即段选择符)的Index字段去索引段描述符表,若TI=0,索引全局段描述符表,TI=1,索引局部段描述符表,表的地址在相应的寄存器中。通过Index字段和段描述符表的位置能找到某项具体的段描述符。将段描述符中的base字段和逻辑地址中的offset字段合并即得到了线性地址。

Intel要求两次转换,这样虽说是兼容了,但是却是很冗余,呵呵,没办法,硬件要求这样做了,软件就只能照办,怎么着也得形式主义一样。

另一方面,其它某些硬件平台,没有二次转换的概念,Linux也需要提供一个高层抽像,来提供一个统一的界面。所以,Linux的段式管理,事实上只是“哄骗”了一下硬件而已。

按照Intel的本意,全局的用GDT,每个进程自己的用LDT——不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段,对应的,内核中的是内核数据段和内核代码段。

1234

__USER_CS index= 14 T1=0

__USER_DS index= 15 T1=0

__KERNEL_CS index= 12 T1=0

__KERNEL_DS index= 13 T1=0

T1均为0,则表示都使用了GDT,再来看初始化GDT的内容中相应的12-15项(arch/i386/head.S):

1234

.quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */

.quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */

.quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */

.quad 0x00cff2000000ffff /* 0x7b user 4GB data at 0x00000000 */

87639b0f61f09becbfb953f3f4ae191e.png

对照着段描述符的64位格式发现,四个段的基地址全为0。这样,给定一个段内偏移地址,按照前面转换公式,0 + 段内偏移,转换为线性地址,可以得出重要的结论,“在Linux下,逻辑地址与线性地址总是一致(是一致,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。!!!”所以如果做linux下内核开发,对于上述的x86的段式管理可以完全不用理会,我们可以认为linux根本没有用intel弄出来的这个段式管理,而是以页式管理完成了所有的内存管理工作。

参考:Linux内核情景分析:45 深入理解Linux内核:73

Logo

更多推荐