分段

来源:互联网 发布:淘宝手机单怎么刷步骤 编辑:程序博客网 时间:2024/04/30 09:21

运行在用户态的所有linux进程都使用一对相同的段来对指令和数据寻址,这两个段就是用户代码段与用户数据段,类似的,运行在内核态的所有linux进程都使用一对相同的段来对指令和数据寻址,就是内核代码段与内核数据段。即是上述代码中的四个段。

两行一组表示一个段,第一行指段描述符在GDT中的index,第二行指将放到段寄存器(段寄存器,如CS,的唯一目的是保存段选择符)中的内容。因为段寄存器的高13位表index,低2位表示权限,第三位表示表指示器(TI),所以把index左移3位(这是上述代码乘8的目的),再设置权限。

后面再+3表示用户段的段寄存器的TI+RPL为011,表示在GDT中,同时RPL为三级,也就是用户空间的。
加0加1是表示在GDT中相对GDT_ENTRY_KERNEL_BASE这个index的偏移,权限是设置在第二行。加3就是用户态,不加等于是加0,就内核态。

段选择符为16位。为了方便查找段选择符,CPU提供了段寄存器来存放段选择符。段寄存器有cs, ss, ds, es, fs, gs(为16位),主要的有cs: 代码段寄存器,包含程序指令的段;ss:栈段寄存器,指向当前程序栈的段。ds:数据段,指向静态数据或者全局数据段。

其中cs寄存器还有一个重要的功能:他含有一个两位的字段,用以指明CPU的当前特权级。

段选择符:


TI表示段描述符是存放在GDT还是存放在LDT,是0表示存放在GDT。否则存放在LDT。
INDEX表示的是选择索引号。就是用此索引号来在GDT中检索相应的段描述符。   
RPL(requested privilege level)用以指明CPU的当前特权级。在linux里面有用户态和内核态两种。这里的RPL(2位)是因为默认的是4级,linux里面使用的是0级来表示内核态,3级表示用户态。内核态能够访问用户态和内核态的地址空间和数据,用户态不可以访问内核态的数据空间(除了使用系统调用这个接口以外)。当相应的段选择符装入到cs寄存器中时指示出CPU当前的特权级。

GDT是全局描述符表,在单处理器系统中只有一个GDT,在多处理器系统中每个CPU对应一个GDT。每个GDT包含18个段描述符和14个空的,其中就包含了4个用户态与内核态下的代码段和数据段。

CPU要访问一个逻辑地址(位于某个段中),要通过load到CS寄存器中的段选择符的index字段,通过该index找到相应段在GDT中的位置,通过该位置就找到了该段的段描述符(对该段的详细描述字段,如下,共8个字节),然后知道了该段的基地址,段大小,段DPL等,从而将CPU要访问的逻辑地址就转化为了线性地址(基地址加段偏移量),然后通过页模式就找到对应的物理地址了。(Protected Mode,内存的管理模式分为两种,段模式和页模式,其中页模式也是基于段模式的)。

段描述符字段:

四个主要的linux段的段描述符字段的值:

从上可知所有的段都是从0地址开始的,这意味着在用户态与内核态所有的进程可以使用相同的逻辑地址。

对于Protected Mode,内存的管理模式分为两种,段模式和页模式。对于段模式来讲,访问一个内存地址仍然使用Segment:Offset的方式,这是很自然的。由于 Protected Mode运行在32-bit系统上,那么Segment的两个因素:Base Address和Limit也都是32位的。IA-32允许将一个段的Base Address设为32位所能表示的任何值(Limit则可以被设为32位所能表示的2^12的整数倍的任何值)。另外,Protected Mode,顾名思义,就是为段访问提供了保护机制,也就说一个段的描述符需要规定对自身的访问权限(Access)。所以,在Protected Mode下,对一个段的描述则包括3方面因素:[Base Address, Limit, Access],它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符。这种情况下,如果我们直接通过一个64-bit段描述符来引用一个段的时候,就必须使用一个64-bit长的段寄存器装入这个段描述符。但Intel为了保持向后兼容,将段寄存器仍然规定为16-bit(尽管每个段寄存器事实上有一个64-bit长的不可见部分,但对于程序员来说,段寄存器就是16-bit的),那么很明显,我们无法通过16-bit长度的段寄存器来直接引用64-bit的段描述符。
怎么办?解决的方法就是把这些长度为64-bit的段描述符放入一个数组中,而将段寄存器中的值作为下标索引来间接引用(事实上,是将段寄存器中的高13 -bit的内容作为索引)。这个全局的数组就是GDT。事实上,在GDT中存放的不仅仅是段描述符,还有其它描述符,它们都是64-bit长,我们随后再讨论。
GDT可以被放在内存的任何位置,那么当程序员通过段寄存器来引用一个段描述符时,CPU必须知道GDT的入口,也就是基地址放在哪里,所以 Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此寄存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。
GDT是Protected Mode所必须的数据结构,也是唯一的——不应该,也不可能有多个。另外,正象它的名字(Global Descriptor Table)所揭示的,它是全局可见的,对任何一个任务而言都是这样。
除了GDT之外,IA-32还允许程序员构建与GDT类似的数据结构,它们被称作LDT(Local Descriptor Table),但与GDT不同的是,LDT在系统中可以存在多个,并且从LDT的名字可以得知,LDT不是全局可见的,它们只对引用它们的任务可见,每个任务最多可以拥有一个LDT。另外,每一个LDT自身作为一个段存在,它们的段描述符被放在GDT中。
0 0
原创粉丝点击