内存寻址

内存地址

  • 逻辑地址:段+偏移量
  • 线性地址:也称为虚拟地址,32位
  • 物理地址:对真正存在的内存条进行寻址的地址
  • 内存管理单元MMU:
    • 分段单元:将逻辑地址转换成线性地址
    • 分页单元:将线性地址转换成物理地址

硬件中的分段

段选择符和段寄存器

  • 段选择符,16位字段
    • 索引号
    • 表指示器(GDT还是LDT)
    • 请求者特权级RPL
  • 段寄存器
    • 目的:存放段选择符
    • 常用段寄存器
      • CS,codeSegment:代码段寄存器,指向程序指令的段
      • SS,stackSegment:栈段寄存器,指向包含当前程序栈的段
      • DS,dataStack:数据段寄存器,指向包含静态数据或全局数据的段
    • PS:CS低二位指明CPU当前特权级CPL

段描述符

描述段的特征,8字节,放在全局描述符表GDT或局部描述符表LDT

  • GDT地址放在gdtr寄存器,LDT地址放在ldtr寄存器
  • 字段
    • Base:段首字节线性地址
    • Limit:最大偏移量
    • DPL:描述符特权级
    • S:系统标志,表明是系统段(GDT、LDT)还是普通段(数据段、代码段)
    • Type:描述段的类型特征和存取权限
    • P:表示是否在内存中,Linux不用
    • D或B:D还是B取决于是代码段还是数据段
    • AVL:Linux不用

分段单元

  1. 检查段选择符TI字段,确定段选择符在GDT还是LDT
  2. 从段选择符的index字段计算段描述符在表中的偏移
  3. 从ldtr寄存器或gdtr寄存器获取段描述符表的基址
  4. 根据2、3找到对应段描述符,Base字段+逻辑地址的偏移量就能得到线性地址

Liunx中的分段

四个重要的Linux段

  • 用户代码段:段选择符宏__USER_CS
  • 用户数据段:段选择符宏__USER_DS
  • 内核代码段:段选择符宏__KERNEL_CS
  • 内核数据段:段选择符宏__KERNEL_DS
  • 四个段描述符的Base和Limit都一样,由CS判断CPL

GDT

每个CPU对应一个GDT,副本基本都一样

  • 内容
    • 用户态和内核态下的代码段和数据段
    • TSS
    • LDT
    • TLS:thread local storage:线程局部存储段,放线程私有数据

LDT

大多数用户态下的Linux程序不使用LDT,因此GDT中的LDT是默认的

硬件中的分页

  • 线性地址被分成以固定长度为单位的组,称为
  • 分页单元把所有物理内存分程固定长的的页框
  • 把线性地址映射到物理地址的数据结构称为页表

常规分页

线性地址被分为3个域,10位目录,10位页表,12位偏移量,4KB一页

  • 二级页表作用:不需要全部页表都放入内存,根据页目录把实际需要的页表放入内存
  • 页目录放在cr3页目录基址寄存器
  • 缺页时把线性地址放在cr2页故障地址寄存器
  • 页目录项和页表项的几个标志
    • present:是否在内存中
    • dirty:是否被写操作
    • read/write:含有页或页表的存取权限
    • pageSize:只应用于页目录项,和下一节扩展分页有关

扩展分页

允许页框大小为4M而不是4KB

  • 这种情况下,不需要中间10位页表进行地址转换
  • 作用:一页太小不好用,避免需要使用大段连续地址时需要过多页表项,占用内存

TLB

用于加快线性地址的转换。每个CPU都有自己的TLB

  • 当cr3寄存器被修改时(进程切换),硬件会自动使TLB的所有项都无效
  • 更新时机:当一个线性地址被第一次使用时,就加入TLB表项
  • 懒惰TLB:避免TLB被刷新
    • 情况:
      • 使用相同页表集的两个进程之间进行切换
      • 普通进程和内核线程进行切换
    • 基本思想:如果几个CPU正在使用相同的页表,而且必须对这些CPU的一个TBL表项刷新,那么运行内核线程的CPU刷新可以延迟
    • 如果下一次进程切换时,下一个进程使用不同的页表,更改cr3寄存器直接刷新TLB
    • 如果下一次进程切换时,下一个进程使用相同的页表,那么这些延迟操作就得实施
posted @ 2021-02-13 19:52  肥斯大只仔  阅读(197)  评论(0编辑  收藏  举报