Linux中的分段

Linux中的分段

什么是段?

有内核数段,内核代码段,用户数据段,用户代码段等,可以帮助理解。

三种地址

首先要区分一下三种不同的地址:
逻辑地址:
包含在机器语言指令中用来用来指定一个操作数或一条指令的地址。每个逻辑地址由一个段和段偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离。
线性地址:
是一个32位无符号整数,可以用来表示高达4GB的地址。
物理地址:
用于内存芯片级的内存单元寻址,与从微处理器的地址引脚发送到内存总线的电信号相对应。

段描述符和GDT、LDT

  程序员都应该知道我们在编程的时候经常会用结构体来代表或者说是描述一个结构。Linux内核中也是如此,比如,Linux中每个进程对应一个进程描述符,包含了进程的全部信息,进程描述符就是一个叫task_struct的结构体。对于分段来说,每个段也应该有它对应的结构体,这个结构应该包含了段的所有属性。这个结构就是段描述符

  段描述符的大小为固定的8字节,它描述了段的特征。段描述符存放在全局描述符表(Global Descriptor Table, GDT)或局部描述符(Local Descriptor Table, LDT )中。GDT和LDP均存放在主存中
  每个CPU通常指定义一个GDT,每个进程除了存放在GDT中的的段之外如果还需要创建附加的段,就可以有自己的LDT。GDT在主存的地址和大小存放在gdtr控制寄存器中,当前正被使用的LDT地址和大小存放在ldtr控制寄存器中。
段描述符中的所有字段如下表所示:
Linux中的分段_第1张图片
在这些字段中比较重要的是DPL字段,这个字段描述了这个段的存取权限,在linux中就是说存取这个段是要在内核态还是内核态和用户态都可以。

段选择符

逻辑地址由两部分组成:16位的段选择符和32位段内偏移量。段选择符的内容如下图:
在这里插入图片描述
我们已经段描述符存放在GDT或者LDT中了,那如何找到我们要找的段在GDT或LDT的哪个位置呢?
逻辑地址中的段选择符就是这个作用。段选择符中的字段描述了对应的段描述符所在的GDT或LDT中的位置。具体见各个字段的详细解释以及后面逻辑地址到线性地址的转换过程。

段描述符中的字段

Linux中的分段_第2张图片
由于一个段描述符是8字节常,因此它在GDT或LDT中的相对地址是由段选择符的索引号的值乘以8得到的。

段寄存器

  为了快速方便地找到段选择符,处理器提供住啊们用于存放段选择符的16位寄存器,这些段寄存器为cs, ss,ds, es, fd, gs。

6个寄存器中有3个有专门的用途:
cs:代码段寄存器,指向包含程序指令的段
ss:栈段寄存器,指向包含当前程序栈的段
ds:数据段寄存器,指向包含静态数据或全局数据集的段
其他3个寄存器为一般用途,可以指向任意的数据段。

  其中cs寄存器中有一个两位的字段(RPL),指明cpu当前处于的特权级(CPL, current privilege level),值0代表最高优先级,值3代表最低优先级。Linux只用0级和3级分别为内核态和用户态

  当CPU的CPL改变时,一些段寄存器的值必须相应地更新,如CPL=3(用户态)时,ds必须保存用户数据段的段选择符,而当CPL=0时,ds寄存器必须包含内核数据段的段选择符。同样的ss寄存器也是一样的。

逻辑地址到线性地址的转换

到这里,我们可以知道如何执行逻辑地址到线性地址的转换了:

  1. 先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中。然后就可以从相应的gdtr或ldtr寄存器中取到GDT或LDT在主存中的地址
  2. 用段选择符的index字段计算段描述的在描述符表中的相对地址:index * 8。然后加上描述符表的地址(gdtr或ldtr中的值)。
  3. 找到段描述符后,段描述符中的base字段记录了段的起始地址,那么,就可以用base加上逻辑地址的偏移量,得到线性地址。

Linux中的分段

2.6版的Linux只有在80x86结构下才需要使用分段。
运行在用户态的所有linux进程都使用用户代码段和用户数据段来对指令和数据进行寻址。类似的,运行在内核态的所以Linux进程都使用内核代码段和内核数据段来对指令和数据寻址。
下表显示了这四个重要的段描述符字段的值:
Linux中的分段_第3张图片
可以看到所有段都是从0x00000000开始,这就说明Linux下逻辑地址与线性地址是一致的,即逻辑地址的偏移量字段和线性地址的值总是一致。

参考文献

深入理解Linux内核

你可能感兴趣的:(Linux内核,操作系统)