内存地址
x86架构包括三种地址:linux
- 逻辑地址
- 用于机器语言寻址,包括操做数的地址及指令地址等。逻辑地址包括短地址以及偏移地址。
- 线性地址(虚拟地址)
- 32位无符号整数,0x00000000~0xffffffff。最多寻址4GB。
- 0x00000000到0xbfffffff,用户态内核态进程都可寻址,0xc0000000到0xffffffff,只有内核态进程才可寻址
- 物理地址
- 用于在存储芯片中寻址存储单元。对应从CPU地址引脚发送到内存总线上的电信号。物理地址表示为32或64位无符号整数。
内存管理单元(MMU)经过分段单元将逻辑地址转换为线性地址,接着又经过页单元将线性地址转换为物理地址。

多CPU系统中,全部CPU共享相同地内存。因此RAM芯片可能同时被多个独立的CPU访问。因为对RAM芯片的读操做和写操做都必须串行地执行,在总线和每一个内存芯片之间添加了一个叫作内存仲裁器的硬件电路。这个电路的做用是:当RAM芯片处于空闲状态时,受权给CPU访问:当RAM芯片处理其余处理器的请求时,延迟其余CPU访问。甚至在单处理器系统中,也会使用内存仲裁器。这是由于这些系统包括了DMA控制器,它与CPU也存在并发操做。固然,多处理器系统中的内存仲裁器电路更为复杂,由于它有更多的输入端口。从编程的角度看,仲裁器是不可见的,由于它彻底硬件电路管理。
硬件分段
段选择符与段寄存器
逻辑地址包括两部分:段标识符
和段内相对偏移地址
。段标识符是16-bit的域,称为段选择符;偏移地址是32-bit。
编程
- index:指明GDT或LDT中的段描述符项。因为段描述符是8字节,它在GDT或LDT中的相对地址经过index字段乘8得到。addr = gdtr(ldtr) + 8*index。GDT最多能够保存2^13-1个段描述符。
- TI:0,GDT;1,LDT
- RPL:当段选择符加载到cs寄存器时,指明CPL;当访问数据段时,也用来选择性地减弱CPL。
为了方便快速检索段选择符,处理器提供了6个专用分段寄存器来保存段选择符,它们是cs,ss,ds,es,fs和gs. 程序能够经过S/L来
重用段寄存器。缓存
- cs:代码段寄存器,指向含有程序指令的段;cs寄存器还有一个重要的功能:它包含一个指定当前CPU特权级(CPL)的的2-bit域。0表示最高优先级;3表示最低优先级。 Linux只使用了0和3,以区份内核模式和用户模式
- ss:堆栈段寄存器,指向包含当前程序栈的段
- ds:数据段寄存器,指向包含静态和全局数据的段
其它三个是通用分段寄存器,能够指向任意类型的段。
段描述符
每一个段都由8字节的段描述符表示,它描述了段的特征。段描述符要么存储在全局描述符表(GDT)里,要么存储在本地描述符表(LDT)里。一般只定义了一个GDT,若是进程须要额外建立除了GDT中以外的段,每一个进程也容许有本身的LDT。主存里GDT的地址和大小都包含在gdtr控制寄存器中,而当前正在使用的LDT的地址和大小则包含在ldtr控制寄存器中。
数据结构
- Base:段首字节的线性地址
- G:若是为0,段大小用字节表示,不然用4096字节的倍数表示
- Limit:段中最后一个内存单元的偏移地址,即段的长度。G为0时,段长度为1~1MB;G为1时,长度为4KB~4GB
- S:若是置为0,这个段是系统段,保存关键的数据结构;不然是普通的代码段或数据段
- Type:指明段的类型及访问权限,包括代码段,数据段,任务状态段以及LDT段
- DPL: 描述符特权级,指明了访问此段所需的最小CPL。0级DPL只能被0级CPL访问;3级DPL能够被任意等级CPL访问
- P:0表明此段不在主存中。Linux不会将整个段交换到硬盘,全部这一字段老是1
- D or B:1表明段偏移是32位,0表明16位
- AVL:Linux中不使用
段描述符的快速访问
前面提到,逻辑地址由16位段选择符和32位段偏移构成,而且段寄存器只用来存储段选择符。
为了加速逻辑地址想线性地址的转换,x86处理器为6个段寄存器各自添加了一个不可编程寄存器,存储了段寄存器中段选择符所指定的段描述符。当段选择符加载到段寄存器时,段描述符被加载到不可编程的段寄存器中。所以,逻辑地址东侧转换不用访问主存中的GDT或LDT。只有当段寄存器改变时,才须要访问GDT或LDT。
架构
分段单元
分段单元经过如下操做将逻辑地址转化为线性地址。
并发
- 检查TL字段,决定从gdtr仍是ldtr获取基址
- index*8加上基址获得段描述符的地址
- 将段描述符Base字段加上offset获得线性地址
因为不可变成寄存器的存在,只有当段寄存器发送改变时才须要作前两步。
Linux分段
Linux以极为有限的方式使用分段,而更喜欢使用分页。操作系统
- 若是全部进程使用相同的段寄存器,即共享一套线性地址时,内存管理更为容易
- Linux的设计目标是对普遍架构提供可移植性,RISC架构对分段支持有限
2.6版本仅在x86架构中使用分段。全部用户态进程使用一样的一对指令段和数据段进行寻址。一样,所用内核态的进程也是如此。对应的段选择符经过4个宏定义:__USER_CS, __USER_DS, __KERNEL_CS,和__KERNEL_DS。与这些分段对应的线性地址都从0起始,而且寻址限制长度为2^32-1,即不管用户态和内核态进程都使用相同的逻辑地址。而且逻辑地址的offset字段与对应的线性地址一致。
硬件分页
分段与分页必定程度上是冗余的,它们均可觉得不一样的进程区分物理地址空间。分段将不一样的线性地址空间赋给每一个进程;分页将相同的线性地址空间映射到不一样的物理地址空间。分页单元将线性地址转换为物理地址。分页单元的主要任务是检查访问类型与访问权限是否匹配。若是内存访问是无效的,将会产生页错误异常。
出于高效,线性地址被分组为固定长度的区间,称为页。同一页中的连续线性地址被映射为连续的物理地址。这样,内核能够指定页的物理地址和访问权限而不用指定页中包含的全部线性地址的物理地址和访问权限。一般,“页”既指一组线性地址,也包含这组地址中的数据。
分页单元将所有RAM分红固定大小的页框(物理页)。每一个页框包含一个页。页框是主存的组成部分,所以也是存储区域。页和页框的区别在于,前者只是一个数据块,能够存放在任何页框或硬盘上。将线性地址映射为物理地址的数据结构称为页表。页表存储在主存中,而且在启用分页单元以前应当被内核初始化。从80386开始,x86处理器支持分页,经过置位cr0寄存器的PG标志启用,若PG为0,线性地址被解释为物理地址。设计
常规分页
从80386开始,分页单元处理4KB的页。32位线性地址被分为3个字段。3d
- 目录:高10位
- 页表:中间10位
- 偏移:低12位
线性地址的转换由两步完成,第一步是页目录,第二步是页表。两级模式减小了每一个进程页表所需的RAM空间。一级模式若进程使用整个4G地址空间,则须要2^20个表项,二级模式能够只为进程实际使用的虚拟内存区请求页表。每一个活动的进程都要有页目录,不须要立刻为全部页表分配内存,只有当进程实际须要页表的时候再为其分配内存会更加有效率。
正在使用的页目录的物理地址存在cr3寄存器中。线性地址中的Dirtecory字段决定页目录中的目录项,目录项指向页表。Table字段决定页表中的表项,包含页框所在的物理地址。Offset字段决定页框中的相对位置。

页目录和页表具备相同的结构。目录项与页表项分配给进程的物理地址。其他项均填0,访问这些项的线性地址将产生缺页异常:指针
- Present:若是置位,代表所指的页或页表包含在主存中;若是为0,页不在主存,其余位可被操做系统使用。若须要地址转换的目录项或页表项中Present被重置,线性地址存在cr2中,产生缺页异常
- Accessed:当分页单元对页框进行寻址时置位。用于操做系统换出页。重置由操做系统完成
- Dirty:只用于页表项。当页框执行写操做时置位,用于操做系统换出页。重置由操做系统完成
- Read/Write:页或页表的读写权限。若为0,只读,不然,可读写
- User/Supervisor:访问页或页表所需的特权级。若为0,CPL小于3(内核态)才能对页寻址,不然,总能对页寻址
- PCD和PMT:控制硬件高速缓存处理页或页表的方式
- Page Size:只用于页目录项,若是置位,页目录项指的是2M或4M页框
- Global:只用于页表项,防止经常使用页从TLB高速缓存中刷新出去
扩展分页
x86微处理器提供扩展分页,容许页框大小达到4MB,要求Page Size字段置位。内核不使用中间页表,节省内存并保留了TLB项。

- Directroy:高10位
- Offset:低22位
物理地址扩展分页机制
处理器支持的RAM容量受地址总线上的地址引脚数量限制。Intel将引脚数量由32扩展到36位支持64GBRAM,做为32位x86架构的扩展。须要引进新的分页机制将32位线性地址转换为36位物理地址。
- 64GBRAM分为2^24个页框。页表项的页字段由20位扩展到24位。所以页表项有32位变为64位。
- 引入新级别页目录指针表(PDPT),由4个64位表项组成
- cr3存储27-bit的PDPT基址
硬件高速缓存
微处理器时钟频率有几个GHz,DRAM存取时间是周期的数百倍。为了缩小CPU与RAM速度不匹配,引入硬件高速缓存内存,基于局部性原理。使用小而快的内存存放最近最经常使用的代码与数据。当访问RAM时,CPU分为命中高速缓存与没有命中。当命中时,控制器从高速缓存行中取数据到CPU寄存器。对于写操做,分为通写和回写。通写既写高速缓存行也谢RAM。回写只更新高速缓存行,只有当CPU执行刷新指令或FLUSH硬件信号(一般不命中)产生写回到RAM中。Linux对全部页框都启用高速缓存,对全部写操做采起回写策略。在多处理器系统中,每一个处理器都有单独的硬件高速缓存,若是一个CPU修改了高速缓存,其余包含这块数据的CPU须要保持数据同步,称为高速缓存侦听。

TLB:转换后援缓冲器,用于加快线性地址的转换。当线性地址经过计算得出的物理地址存放在TLB表项中。多处理器系统中,每一个CPU都有本身的TLB,无需同步。当cr3寄存器修改时,本地TLB全部项失效,由于新的一组页表被启动。
Linux中的分页
Linux使用了常规分页。为了适用32位于64位架构,采用4级分页模型:
- 页全局目录
- 页上级目录
- 页中间目录
- 页表
对于未启用物理地址扩展的32位系统,2级分页已经足够。Linux将上级和中间目录置为0。可是两个目录在指针序列中的位置被保留,所以它们能够在32和64位系统上使用。内核保留上级和中间目录,将它们项数设为1,而且映射到全局目录的表项中。 对于启用物理地址扩展的32位系统,使用3级分页。全局目录与x86的页目录表一致,上层目录被省略,中间目录对应x86页目录,页表对于x86页表。64位系统取决于硬件对线性地址位的划分。 Linux进程处理很大程度依赖于分页。给每一个进程分配不一样的物理地址空间,有效防止寻址错误;能够将页框中的页保存到磁盘上,再从新装入其余页框中。每一个进程都有本身的页全局目录和页表,发生进程切换时,将cr3寄存器保存在换出进程描述符中,将换入进程对应的值存入cr3。