逻辑地址如何转换为线性地址
一个逻辑地址由两部份组成,段标识符: 段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节,最后两位涉及权限检查。如图:
索引号就是“段描述符(segment descriptor)”。段描述符描述了一个段。这样,很多个段描述符,就组了一个数组,叫“段描述符表”。然后可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。每一个段描述符由8个字节组成,如下图:
这里我们只关心Base字段,它描述了一个段的开始位置的线性地址。
Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中。一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。那究竟什么时候该用GDT,什么时候该用LDT呢?这是由段选择符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。
GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。
下面是一个例子:
首先,给定一个完整的逻辑地址[段选择符:段内偏移地址]
- 看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。
- 拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。
- 把Base + offset,就是要转换的线性地址了。
Linux的段式管理
按照Intel的本意,全局的用GDT,每个进程自己的用LDT——不过Linux则对所有的进程都使用了相同的段来对指令和数据寻址。即用户数据段,用户代码段,对应的,内核中的是内核数据段和内核代码段。
在Linux下,逻辑地址与线性地址总是一致(是一致,不是有些人说的相同)的,即逻辑地址的偏移量字段的值与线性地址的值总是相同的。在Linux中,绝大部份进程并不例用LDT。