《深入理解linux内核》读书笔记 -- 第二章 内存寻址

时间:2023-02-15 11:58:37

地址概念:

  • 逻辑地址,由段标识符+偏移量组成;
  • 线性地址,又称虚拟地址;
  • 物理地址,用于内存芯片寻址。

为什么需要虚拟内存?

CPU 对内存的寻址最简单的方式就是直接使用物理内存地址,这种方式一般叫做物理寻址。早期的 PC 使用物理寻址,而且像数字信号处理器、嵌入式微控制器也使用物理寻址。物理寻址的好处是简单,坏处也有很多,比如:

不安全:操作系统的地址直接暴露给用户程序,用户程序可以破坏操作系统。这种解决方案是采用特殊的硬件保护。

同时运行多个程序比较困难:多个用户程序如果都直接引用物理地址,很容易互相干扰。那么是不是可以通过不断交换物理内存和磁盘来保证物理内存某一时间*一个程序在运行呢?当时是可以的,但是这引入很多不必要和复杂的工作。

用户程序大小受限:受制于物理内存大小。我们现在的错觉是应用程序大小都小于物理内存,这主要是因为现在 PC 的物理内存都比较大。实际上只有 1G 物理内存的 PC 是可以运行 2G 的应用程序的。

《深入理解linux内核》读书笔记 -- 第二章 内存寻址

综合上面各种缺点,虚拟内存出现了。


MMU:

MMU(内存管理单元)的功能是将逻辑地址转换成物理地址。其中包括:

  • 分段单元,将逻辑地址转换成线性地址;
  • 分页单元,将线性地址转换成物理地址;《深入理解linux内核》读书笔记 -- 第二章 内存寻址
逻辑地址到线性地址的转换:通过分段机制

《深入理解linux内核》读书笔记 -- 第二章 内存寻址 
段标识符: | index<15-3> |ti<2> | RPL<1-0> | 
段描述符: 
《深入理解linux内核》读书笔记 -- 第二章 内存寻址 
(段标识符->index*8+段选择符gdtr或者ldtr寄存器中的基地址) –>得到段描述符–>段描述符->base+offset —>线性地址。

进程的段

每个 Linux 程序都有一个运行时内存映像,也就是各个段的布局,简单如下图所示。

《深入理解linux内核》读书笔记 -- 第二章 内存寻址

注意上图只是一个相对位置图,实际上这些段并不是相邻的。主要的段包括只读代码段、读写段、运行时堆、用户栈。在分配栈、堆段运行时地址的时候,

链接器会使用空间地址空间布局随机化(ASLR),但是相对位置不会变。上图中 .data 等是对应进程中的不同数据的 section ,或者叫做节。简介如下。

  • .text: 已编译程序的机器代码。
  • .rodata: 只读数据。
  • .data: 已初始化的全局和静态变量。局部变量保存在栈上。
  • .bss: 未初始化的全局和静态变量,以及所有被初始化为 0 的全局或者静态变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。

线性地址到物理地址的转换过程:

硬件分页

把线性地址分成以固定长度为单位的组。再映射到连续的物理地址中。

  • page frame (页框) ram 被分成固定长度
  • page 每个页框包含一个页
  • page table 线性地址映射到物理地址的数据结构称之为页表

- 常规分页 
《深入理解linux内核》读书笔记 -- 第二章 内存寻址

32位线性地址分为三个域: 
Directory 最高10位 
Table 中间10位 
Offset 最低12位 
Directory和Table有相同的数据结构

  • present 0 不在主存中(线性地址保存到cr2 产生缺页异常) 1 在主存中
  • Accessed
  • dirty
  • Read/Write 读写执行权限
  • User/Supervisor 硬件保护
  • PCD/PWT
  • Page Size 页大小
  • Global

- 扩展分页 
Directory 最高10位 
Offset 最低22位 
页大小由4KB变为4MB

注:cr4 PSE控制分页模式

- 物理扩展分页 PAE 
目的: 为了支持4GB以上内存寻址,可高达64GB寻址。 
32位线性地址划分为: 
cr3 -> PDPT 
31-30 指向PDPT4个项中的一个 
29-21 页目录512项中的一个 
20-12 页表中512项中的一个 
11-0 Offset

通过改变cr3 或者 PDPT的值 就可以寻址4GB以上内存


多级页表。以两级页表为例。一级页表中的每个 PTE (page table entry)映射虚拟地址空间的一个 4MB 的片,每一片由1024 个连续的

页面组成。一级 PTE 指向二级页表的基址。这样 32 位地址空间使用 1024 个一级 PTE 就可以表示。需要的二级页表总条目还是

2^32 / 2^12 = 2^20 个。这里的关键在于如果一级 PTE i 中的页面都未被分配,一级 PTE 就为空。多级页面的一个简单示意图如下。

《深入理解linux内核》读书笔记 -- 第二章 内存寻址

多级页表减少内存占用的关键在于:

  1. 如果一级页表中的一个 PTE 为空,那么相应的二级页表就根本不会存在。这是一种巨大的潜在节约。
  2. 只有一级页表才需要常驻内存。虚拟内存系统可以在需要时创建、页面调入或者调出二级页表,从而减轻内存的压力。

几种加快内存访问速度的技术

-硬件高速缓存 
高速缓存在分页单元与主内存之间,有一个高速缓存内存与一个控制器组成。控制器存放着一组缓存表项,每个表项对应着高速缓存内存中的一行,每个表项由一个标签和几个flags组成。每次提出物理地址后,高几位与标签比较,如果一致则命中高速缓存。

-转换后援缓存器(TLB) 
目的:加快线性地址到物理地址的转换。在寻址到物理地址后,将物理地址存放在TLB表项中,将来对同一线性地址寻址时,直接从TLB中提取出。

TLB(Translation Lookaside Buffer)。加入 TLB,整个虚拟地址翻译的过程如下两图所示。

《深入理解linux内核》读书笔记 -- 第二章 内存寻址

《深入理解linux内核》读书笔记 -- 第二章 内存寻址