《深入理解linux内核》学习 — 内存寻址

以内存为开始是正确的学习顺序

内存地址

首先引出三种地址:

  1. 逻辑地址

包含在机器语言指令中用来指定一个操作数或一条指令的地址。每个逻辑地址都由一个段和偏移量组成,它支持程序员把程序分成若干段。

  1. 线性地址:

也可以称它为虚拟地址

  1. 物理地址:

顾名思义

内存控制单元(MMU)通过分段单元把逻辑地址转换为线性地址;通过分页单元把线性地址转换为物理地址。

硬件中的分段

段标识符

逻辑地址由16位的段标识符(也成为段选择符)和一个32位的相对地址偏移量表示

CPU有专门的段处理器存放段选择符(cs,ss,ds,es,fs,gs)。

cs:代码段寄存器

ss:栈段寄存器

ds:数据段寄存器

其余:指向任意数据段

段描述符

找到对应的段,可以通过段描述符了解段的的特征(进程有进程描述符,段自然也有段描述符)。

段描述符放在全局描述符表(GDT)或是局部描述符表(LDT)中。通常只定义一个GDT,每个进程除了存放在GDT中的段之外,如果还需要创建新的段,就可以放在自己的LDT中。

GDT在主存中地址和大小存放在gdtr控制寄存器中;当前被使用的LDT地址和大小放在ldtr控制寄存器中。

之前16位的段选择符前13位表示段描述符在GDT中的下标,所以GDT最多保存2^13 – 1个段描述符,因为一个段描述符是8字节长,加入GDT地址位0x00020000,我们要获取下标为2的段描述符,要访问的地址为0x00020000 + (2 * 8)。

分段单元

分段单元负责将逻辑地址转换为线性地址,过程如下:

  1. 先检查TI字段,判断段描述符在GDT还是LDT,从对应寄存器拿到GDT或LDT的地址
  2. 根绝段选择符的索引号在GDT或LDT中得到段描述符地址
  3. 将逻辑地址中的偏移量 + 段描述符中的base地址,得到线性地址

Linux中的分段

分段可以给每个进程分配不同的线性地址空间,分也可以把一个线性地址空间映射到不同物理空间。

Linux设计目标之一是把它移植到绝大部分流行的处理器平台上,然后RISC对分段的支持有限,所以只有在90×96结构下Linux才使用分段,其实都只是用分页(此处指的是Linux2.6)。

当所有进程使用相同的段寄存器时,内存管理变得更简单。运行在用户态的所有Linux进程都只使用一堆段来对指令和数据寻址:用户代码段;用户数据段,内核态中进程则使用:内核代码段;内核数据段。

Linux下逻辑地址与线性地址是一样的,所有段都从0x00000000开始,逻辑地址的偏移量的值与线性地址的值总是一致的。

硬件中的分页

这块就很熟悉了,页目录 + 页 + 偏移量,目录可以是多级的,还是比较经典的。

分页单元吧所有RAM分成固定长度的页框,每个页框包含一个页。

从线性地址到物理地址需要一个映射表,我们称之为页表,放在主存中。

其余组成比如高速缓存,TLB,一个是为了缩小CPU与RAM之间的速度不匹配,另一个是辅助快速从线性地址翻译成物理地址。

Linux中的分页

Linux从2.6.11版本开始采用四级分页模型:

  1. 页全局目录(PGD)
  2. 页上级目录(PUD)
  3. 页中间目录(PMD)
  4. 页表(PTE)

至于具体给每个部分分配多少位数,要看计算机体系结构,逻辑上是有的,但实现上可能没有。

内核页表

内核维持着一组自己使用的页表,内核映像刚被装入内存中时,CPU还运行在实模式,分页功能没被启用:

  1. 内核创建一个有限的地址空间,包括内核的代码段核数据段、初始页表核用于存放动态数据结构共128KB大小的空间
  2. 用剩余的RAM建立分页表

当进程运行在用户态时,产生的线性地址小于0xc0000000;当进程运行在内核态时,产生的线性地址大于等于0xc0000000,进程在线性地址空间中的偏移量时0xc0000000,也是内核生存空间的开始处。

内核页表所提供的最终映射必须把0xc0000000开始的线性地址转化为从0开始的物理地址。

从网上找了个图,便于了理解,RAM上3GB~4GB是留给内核的。

进一步了解RAM留给内核页表(3GB~4GB中前896MB)的划分:

  1. 要是计算机有小于896MB的RAM,那直接和物理地址一一对应即可
  2. 要是896MB~4096MB之间,那32位也可以表示全部的物理地址,只是需要时不时改变页表项
  3. 超过的话就需要用上预留的的128MB了,做额外的操作(具体操作见书本)

TLB

介绍完了多级页目录,就更能理解TLB优势了。

在硬件上会有一个叫做页表基地址寄存器,它存储PGD页表的首地址。然后PGD->PUD->PMG->PTE,非常繁琐。

TLB可以直接缓存虚拟地址核其映射的物理地址,大大降低了查找地址的延时。

关于TLB flush等概念可以参考链接:https://zhuanlan.zhihu.com/p/108425561,言简意赅。

留下评论