摘自《深入理解linux内核》
程序通过内存地址(memory address)访问内存单元,而内存地址分为三种:
- 逻辑地址(logical address):机器指令使用的地址,由段(segment)和偏移量(offset)组成
- 线性地址(linear address):32位无符号整型,可表示4GB地址,范围0x00000000 到0xffffffff
- 物理地址(physical address):用于芯片级寻址,对应于微处理器地址引脚到总线上的电信号。
以上流程由内存控制单元(MMU)完成。而内存地址分段涉及从逻辑地址转为线性地址的过程。
内存仲裁器
多处理器系统中,CPU共享一个内存,每个RAM芯片与总线之间的内存仲裁器(memory arbiter)用于控制芯片的并发访问,若RAM空闲,则允许CPU访问,否则延迟访问。单处理器系统也有仲裁器,因为DMA控制器也会和CPU并发访问RAM。
硬件分段
逻辑地址由两部分组成:段标识符(选择符)和段内偏移
段选择符
16位长度,结构如下:
处理器用段寄存器存放段选择符以便快速获取
段寄存器
有6个,cs、ss、ds、es、fs、gs,其中3个有专门用途:
- cs:代码段寄存器,指向包含程序指令的段;还有指定特权的2位字段(CPL),值越小优先级越高,0为内核态,3为用户态
- ss:栈段寄存器,指向包含当前程序栈的段
- ds:数据段寄存器,指向包含静态数据或全局数据的段。
段描述符
8字节长,描述段特征,放在全局描述符表(gdt)或者局部描述符表(ldt)。
gdt一般只有一个,若进程有自己的段,则可以有ldt。
gdt在主存的位置和大小放在gdtr 控制寄存器中,当前使用的ldt放在ldtr控制寄存器。
段描述符每个字段的含义:
linux主要的段描述符有:
- 代码段描述符
- 数据段描述符
- 任务状态段描述符
段描述符格式如下:
段描述符的快速访问
80x86处理器提供一个非编程寄存器供可编程寄存器(前面6个)使用,用于存放8字节段描述符,在段选择符装入段寄存器时就装入对应的段描述符。若段寄存器内容不变,则不用访问gdt或者ldt,直接取非编程寄存器的值。
段选择符有以下字段:
段描述符是8字节长,因此它在gdt或ldt的相对位置由段选择符的高13位值乘以8得到。
分段单元
分段单元(segment unit )用于将逻辑地址转为线性地址,步骤如下:
- 读段选择符TI段内容,决定是找gdt还是ldt。
- 从段选择符index字段计算段描述符相对地址,index 乘以8,与gdtr或ldtr寄存器的值相加,得到段描述符的地址
- 取出段描述符中base字段值,与逻辑地址偏移相加得到线性地址