硬件中的分段
逻辑地址由一个16位的段选择符(segment selector)和32位的偏移量(offset)组成。偏移量就是段内相对地址。
段寄存器就是存放段选择符的,包括cs、ss、ds、es、fs、gs。其中:
cs(代码段寄存器):指向程序指令段;
ss(栈段寄存器):指向当前程序栈的段;
ds(数据段寄存器):指向包含静态数据or全局数据段。
cs寄存器还有一个字段包含CPU当前特权等级(Current Privilege Level,CPL)。值0为最高优先级,值3为最低优先级。Linux只有0级和3级,分别称为
内核态和用户态。
段描述符
每个段由一个8字节的段描述符(segment descriptor)表示。
段描述符放在
全局描述符表(Global Descriptor Table,GDT)或
局部描述符表(Local Descriptor Table,LDT)中。
GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前正在使用的LDT地址和大小放在ldtr控制寄存器中。
Linux中的分段
Linux对分段支持很有限,2.6kernel中只有8086结构下才使用分段,其余使用分页方式。
运行在用户态的Linux进程用两个相同的段对指令和数据进行寻址,这两个段就是
用户代码段和
用户数据段。在内核中同样有内核代码段和内核数据段。
硬件中的分页
分页单元(paging unit)把线性地址转为物理地址。把所请求的访问类型与线性地址的访问权限相比,如果访问无效,则产生一个
缺页异常。线性地址被分为固定长度为单位的组,称为
页(page)。分页单元把所有的RAM分成固定长度的
页框(page frame),也称为
物理页。每一个页框包含一个页。
把线性地址映射为物理地址的数据结构成为
页表(page table)。页表存放在主存中,在启用分页单元前由内核进行初始化。
常规分页
从80386起,Intel CPU支持4KB的页。
为了减少每个进程页表所需RAM数量,线性地址转换分为两步:页目录表(page directory)->页表(page table)。(与上面的页表不一个概念)。正在使用的页目录的物理地址存放在控制寄存器cr3中。线性地址内的Directory字段决定页目录中的目录项,目录项指向适当的页表。线性地址的Table字段决定页表中的表项,表项含有页所在页框的物理地址。Offset字段决定页框内的相对位置。Offset是12位,因此每一页容量为4096字节。Directory和table分别是10位,因此寻址范围为:1024×1024×4096=2^32。
扩展分页
从Pentium开始,引入了
扩展分页,允许页框的大小由4KB变为4MB,用于把大段连续的线性地址转为物理地址。内核因此可以不用中间页表进行地址转换,节省内存,保留TLB项。
转换后援缓冲器(Translation Lookaside Buffer,TLB,俗称“
快表”),是一个高速缓存,用于加快线性地址的转换。
物理地址扩展(Physical Address Extension,PAE),允许内核使用高达64GB的RAM。但是,它针对的是物理地址,并没有扩大用户态程序的线性地址范围。
Reference
1、深入理解Linux内核-第3版