内存寻址
内存地址
逻辑地址
机器语言指令中用来指定一个操作数或者一条指令的地址
每一个逻辑地址由一个段和偏移量组成
偏移量指明了从段开始的地方到实际地址之间的距离.
线性地址(虚拟地址)
32位无符号整数,可以表达4GB的地址
物理地址
内存芯片级单元寻址
关系:
分段机制:逻辑地址->线性地址
分页机制:线性地址->物理地址
段选择符和段寄存器
逻辑地址组成:段标识符+偏移量
段标识符16位长,称为段选择符.偏移量32位长.
为了快速找到段选择符,提供段寄存器
段寄存器目的:存放段选择符
段寄存器只有6个,但有3个有专门用途:
cs 代码段寄存器 指向包含程序指令的段
ss 栈段寄存器 包含当前程序栈的段
ds 数据段寄存器 包含静态数据或者全局数据段
cs段有一个两位字段(CPL),0表示最高优先级(内核态),3表示最低优先级(用户态)
段描述符
每个段由一个8byte的段描述符表示,用于描述段的特征
它描述了段的特征,段描述符放在全局描述符表(GDT)或者局部描述符表(LDT)中
通常只定义一个GDT,每个进程除了存放在GDT中的段之外如果还需要创建附加的段,就可以有自己的LDT.
快速访问段描述符
逻辑地址48bit:16bit的段选择符+32bit的偏移量
段寄存器存放段选择符
分段单元
1.先检查段选择符的TI字段,以决定段描述符保存在哪一个描述符表中.TI字段指明描述符是在GDT中还是在LDT中,
2.段选择符的index字段*8与gdtr或者ldtr寄存器中内容相加
3.把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址
Linux的分段
分段和分页实际上都是划分进程的物理地址空间:
分段可以给每个进程分配不同的线性地址空间
分页可以给同一线性地址空间映射到不同的物理空间
所有段从0x00000000开始,linux下逻辑地址和线性地址是一致的,即逻辑地址的偏移量字段的值与相应的线性地址的值总是一致的.
段寄存器会随着内核特权级别而更新.当为用户态的时候,ds寄存器必须含有用户数据段的段选择符,ss也是.以此类推,内核态也是这样.
当对指向指令或者数据结构的指针进行保存的时候,内核不需要为其设置逻辑地址的段选择符.因为已经保存在了cs寄存器中
Linux GDT
单处理器系统中只有一个GDT,多处理器系统中每个CPU对应一个GDT
分页机制
分页单元把线性地址转换为物理地址
线性地址分为固定长度的页,页内部的连续线性地址被映射到连续的物理地址当中.
页既指一组线性地址,又指包含在这组中的数据.
然后分页把所有RAM分为固定长度的页框,每一个页框包含一个页,也就是说一个页框的长度与一个页的长度一致.
页只是一个数据块,可以存放在页框或者磁盘中
而页框是主存中的物理地址
TLB
一个高速缓存,用于加快线性地址的转换.
当一个线性地址第一次使用的时候,可以通过访问RAM中的页表计算出相应的物理地址.
同时物理地址被存放在一个TLB表项中.
物理内存布局
初始化阶段,内核必须建立一个物理地址映射来指定哪些物理地址范围内对内核可用那些不可用.
进程页表
进程的线性地址空间分为两部分.
从0x00000000到oxbfffffff的线性地址.无论进程运行在用户态还是内核态都可以寻址
从0xc0000000 到0xffffffff的线性地址,只有内核态的进程才能够寻址
进程运行在用户态的时候,产生的线性地址小鱼0xc0000000,进程运行在内核态的时候,执行内核代码,产生地址大于等于0xc0000000.
内核页表
内核维持着一组自己使用的页表,驻留在所谓的主内核页全局目录中,系统初始化后,这组页表还从来没有被任何进程或者线程使用,第一个阶段内核创建一个有限的地址空间,这个空间仅能够装入RAM和对其初始化的核心数据结构.
第二个阶段,内核充分利用剩余的RAM并且适当地建立分页表.
固定映射的线性地址
至少128MB的线性地址总是留作他用,因为内核使用这些线性地址实现非连续内存分配和固定映射的线性地址.
内核使用固定映射的线性地址来代替指针变量,因为这些指针变量的值从来不改变.