Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

地址转换 #2

Open
linj-disanbo opened this issue Sep 24, 2020 · 0 comments
Open

地址转换 #2

linj-disanbo opened this issue Sep 24, 2020 · 0 comments
Labels
os 梳理操作系统的知识点

Comments

@linj-disanbo
Copy link
Owner

linj-disanbo commented Sep 24, 2020

当我们在程序中读取一个变量时, 会有两个概念性的事情发生.

  1. 地址转换: 变量的地址要和物理地址对应起来
  2. 去物理地址读取内容. 由于存储体系有各种缓存, 需要从离CPU近的缓存开始找对应的内容.

地址

从程序员角度看, 常见的地址有编程语言中的指针地址, 汇编中的偏移量.
在操作系统中, 会有逻辑地址, 线性地址, 物理地址的不同概念.
在硬件中对应的是内存的存储单元.

指针是对地址的抽象, 具体表示什么, 和运行环境有关.

  1. 在嵌入式开发在没有MMU的情况下, 指针表示物理地址
  2. 一般表示逻辑地址.

各类地址概念

  1. 逻辑地址(logical address): 在机器语言中, 相对某个段的偏移. 段如 代码段, 数据段.
  2. 线性地址(linear address): 32位系统, 0-4G整数
  3. 物理地址(physical address): 对应在硬件芯片中的存储单元

为什么需要地址转换

应用程序变大后, 导致物理内存不够用, 不能直接将程序全部加载到内存中, 所以将想出来将应用程序用到的部分先加载的办法.
程序部分加载导致需要记录哪些部分已经加载, 哪些部分不用或暂时不用先保存到外部存储上, 这部分功能就演变成现在的地址转换和内存管理.

分页和分段

地址转换分为分页和分段.

从概念上看: 程序 -> 段 -> 页 -> 内存. 操作系统靠分页和分段完成程序地址到物理地址的转换.

  1. 操作系统用段管理程序, 段可以描述有逻辑相关的地址的集合, 如代码段, 数据段
  2. 操作系统按页管理内存资源, 页对应物理上相连的地址. 段的概念比页更抽象.

实现原理

硬件提供支持

  1. 分段单元: 逻辑地址到线性地址转换
  2. 分页单元: 线性地址到物理地址转换
  3. 不可编程的寄存器: 包含段描述符
  4. 高速缓存: 在分页单元和内存之间, 按行缓存内存
  5. TLB: 地址变换高速缓存, 缓存页表, 快速完成线性地址到物理地址的转换. (地址转换后援缓冲器 Translation Lookaside Buffer)

转换逻辑如下

  1. 原始的转换逻辑(在没有不可编程的寄存器和TLB参与的情况下), 逻辑地址需要经过分段单元和分页单元得到物理地址
  2. 不可编程的寄存器: 缓存会缓存当前段寄存器对应的段描述符, 可以直接计算线性地址
  3. TLB缓存页表可以加速线性地址到物理地址的计算. (分页单元访问页目录+页表,才能查到页地址)
  4. cache 按行(Cache Line)缓存内存的内容, 即不需要从内存中搬运数据到CPU (这里讲的是按物理地址缓存, 也有资料说cache可以按线性地址缓存)
   --->|不可编程的寄存器|-----                         cache
   |                       |                           ^
   |                       V                           |
逻辑地址 --> |分段单元| --> 线性地址 --> |分页单元| -->  物理地址  --> 读写对应内存
                            |                        ^
                            |--------| TLB   | ------|

分段单元: 从逻辑地址到线性地址

逻辑的地址 由 16位段选择器 + 32位段内偏移

  1. 段选择器保存段寄存器中
  2. 段描述表包含很多段描述符
    1. 段描述符: 8字节
    2. 表分全局描述符表(全局一个)和局部描述符表(一个进程一个)
  3. 段选择器指向 段描述表中的段描述符
  4. 段描述符记录了段的信息, 包括 段的起始地址
  5. 段的起始地址 + 32位段内偏移 = 线性地址

常用段寄存器:

  1. cs: 代码段寄存器
  2. ss: 栈段寄存器
  3. ds: 数据段寄存器
  4. es, fs, gs

寄存器复用套路: 先保存在内存中, 在使用新值, 在回复原值

优化: 存在不可编程的寄存器, 保存在段描述符, 在段寄存器没有发生变化时, 可以直接访问该不可编程的寄存器, 直接获得段信息

分页单元: 线性地址到物理地址

  1. 页: 指一系列线性地址, 也指一组地址中的值
  2. 页框: page frame, 指固定长度RAM. 页和页框长度一样. 页可以保存在页框中, 也可以被交换到磁盘上
  3. 页表: 记录线性地址到物理地址的对应数据结构. 放在RAM 中
  4. CR0 PG flag 是否开启分页

以32位地址为例 (64位系统, 会有多级页表, 原理一致)

  1. 目录 10位
  2. 页表 10位
  3. 偏移量 12位

转换过程

  1. CR3: 页目录表的物理地址
  2. 高10位, 从页目录表找到对应页表
  3. 中10位, 从页表中找到对应的页
  4. 偏移量 + 页地址 = 物理地址

TLB

  1. 线性地址在第一次使用时, 通过分页单元计算成物理地址, 将线性地址到物理地址的缓存的对应关系保存在TLB, 加速线性地址的转化
  2. 页目录的变化, TLB 失效. 一般由于进程使用的独立的页表, 在进程切换后, TLB失效.
  3. TLB 是操作系统可以控制, 需要操作系统调用指令使之失效.

高速缓存

  1. 单位: 按行 (如32字节)
  2. 内存和缓存数据传递
  3. 缓存的映射关系: 全相关, N路相关
  4. 高速缓存是操作系统不能控制的, 是否失效由CPU自行控制. 多CPU间还需要有复制的协议进行同步.

linux 系统

在地址转换上, 硬件提供基础功能(如分段单元, 分页单元) 把这些运算做到硬件上, 可以做到比软件计算更快.
操作系统需要维护下列数据结构:

  1. 全局描述符表: Global Descriptor Table. GDT 全局一个 (系统一个). 对应寄存器: gdtr
  2. 局部描述符表: Local Descriptor Table. LDT 一个进程一个: 对应寄存器: ldtr
  3. 页目录表
  4. 页表

linux (2.2 之后)把所有段的基地址(首字节地址)设置为0, 逻辑地址数值上和线性地址一样.

转换图

转换图

权限检测

  1. CPU运行的特权位记录在 cs 寄存器中
  2. 对应地址的访问权限记录在对应的页表, 缓存记录等中的对应位置
  3. 权限的检测是硬件自动完成的.
@linj-disanbo linj-disanbo added the os 梳理操作系统的知识点 label Sep 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
os 梳理操作系统的知识点
Projects
None yet
Development

No branches or pull requests

1 participant