操作系统开发:启用内存分页机制

该系列文章是在学习《操作系统真相还原》时通过阅读后简化并适当描述整理的学习笔记,首先,致敬作者郑刚博士,在读本书时不得不佩服作者计算机底层功力的深厚。


目前我们已进入保护模式,但依然会受到限制,虽然地址空间达到了4GB,但此空间是包括操作系统共享的4GB空间,我们把段基址+段内偏移地址称为线性地址,线性地址是唯一的,只属于某一个进程。

在我们机器上即使只有512MB的内存,每个进程自己的内存空间也是4GB,这是指的虚拟内存空间。

为什么要分页,分段它不香吗?

一直以来我们都是在内存分段机制下工作的,该模式下如果系统里面的应用程序过多,或者内存碎片过多无法容纳新的进程,则可能会出现进程需要等待,或无法直接运行的局面,而内存分页机制,理论上只要4KB内存就可以让程序运行下去。

  • 在分段模式下

  • CPU认为线性地址等于物理地址,而线性地址是由编译器编译出来的,它本身是连续的,所以要想运行程序,物理地址也必须要连续才行,但在多数情况下物理地址并不是连续的。

  • 在分页模式下

  • 线性地址可以连续而物理地址是分散的,我们通过某种映射关系来解除线性地址与物理地址的一一对应,然后通过映射机制将他们重新关联起来,把线性地址关联到任意的物理地址上面,理论上只要4KB内存就可以运行程序了。

对于分页机制来说,CPU在硬件层面提供了支持,在CPU实现中,这种映射关系是通过页表来实现的,由于地址转换的实时性较高,所以查找页表的功能也是被CPU硬件支持的。

什么是一级页表,它的作用是啥 ?

在保护模式中段寄存器中的内容是段选择子,选择子的最终目的就是为了找到段基址,其内存访问的核心机制依然是,段基址:段内偏移地址,这两个地址相加后才是绝对地址(线性地址),此地址在分段机制下被CPU认为是物理地址可以直接被送上总线,该计算过程也是由段部件自动完成。

分页机制依然要建立在分段机制的基础之上,段部件依然需要工作,而分页只能在分段机制之后进行。

CPU在不打开分页机制的情况下,是按照分段方式工作的,将段地址和段内偏移地址经过段部件处理后所输出的线性地址,CPU就认为是物理地址,
而如果打开了分页,段部件输出的线性地址会变成虚拟地址,虚拟地址不等同于物理地址。

而CPU必须要拿到物理地址才行,此虚拟地址对应的物理地址需要在页表中查找,该工作由页部件自动完成。

  • 分页机制的思想:
  • 通过映射,可以使连续的线性地址与任意物理内存地址相关联,逻辑上连续的线性地址其对应的物理地址可以不连续。
  • 分页作用,将线性地址转换成物理地址,用大小相等的页(页部件)代替大小不相等的段(段部件)。

通常情况下,经过段部件输出的线性地址也可以叫做虚拟地址,所谓的转换是指,从线性地址空间(段)到虚拟地址空间(页)再到物理地址空间(实际),如下图:

前面说过,分页机制建立在分段机制之上,即使在分页机制下的进程也要先经过逻辑上的分段,代码段和数据段在逻辑上被拆分成为以页为单位的小内存块,此时的虚拟地址不能存放任何数据。

接着操作系统开始为这些虚拟内存页分配真实的物理内存页,它查找物理内存中的可用页,然后在页表中登记这些物理页地址,此时就完成了虚拟页到物理页的映射,每个进程都以为自己独享4GB地址空间。

用于存储这种映射关系的表,就是页表(PT),页表中每一行(1个单元格)称为页表项(PTE),其大小是4字节,页表项的作用是存储内存物理地址,当访问一个线性地址时,实际上就是在访问页表项中所记录的物理内存地址。


一个页可被分为高低位吗?

CPU采用一个页的大小是4KB,32位地址表示4GB空间,可将32为地址分成高低两部分,低地址是内存块大小,高地址是内存块数量,故内存块数*内存块大小=4GB,页是地址空间的计量单位,只要是4KB的地址空间都可以称为一页,所以线性地址空间也要对应物理地址空间的一页。

一页大小是4KB(页大小是4KB所以页表项中的物理地址都是4K的整数倍),这样一来,4GB地址空间被划分成4GB/4KB=1MB个页,也就是4GB空间中可容纳1048576个页,页表中自然也要有1048576个页表项,这就是一级页表。

任意一个地址最终都会落到某个实际的物理页上,32位地址空间共有1MB个物理页.

首先定位到某个具体物理页,然后给出物理页内的偏移量就可以访问到任意1字节的内存,虚拟地址的高20位用来定位具体的物理页,低12位则用来在该物理页内寻址。


一级分页中的地址转换过程是怎样的?

页表是位于内存中的,在打开分页前需要将页表物理地址加载到CR3寄存器内,只要提供页表物理地址,便能够访问到页表中任意一个页表项地址。

一个页表项对应一个页,所以,用线性地址的高20位作为页表项的索引,每个页表项要占用 4 字节大小,所以这高20位的索引乘以4后才是该页表项相对于页表物理地址的字节偏移量。用 cr3 寄存器中的页表物理地址加上此偏移量便是该页表项的物理地址,从该页表项中得到映射的物理页地址,然后用线性地址的低 12 位与该物理页地址相加,所得的地址之和便是最终要访问的物理地址。CPU 中集成了专门用来干这项工作的硬件模块,我们把该模块称为页部件。

  • 总结一下页部件的工作
  • 用线性地址的高 20 位在页表中索引页表项,用线性地址的低 12 位与页表项中的物理地址相加,所求的和便是最终线性地址对应的物理地址。


什么是二级页表,它的作用是啥 ?

一级页表与二级页表原理一致,但在现代操作系统中一般都采用二级页表结构,两种页表结构区别如下:

  • 1.一级页表中最多可容纳 1M(1048576)个页表项,每个页表项是 4 字节,如果页表项全满的话,便是 4MB 大小。
  • 2.一级页表中所有页表项必须要提前建好,原因是操作系统要占用 4GB 虚拟地址空间的高 1GB,用户进程要占用低 3GB。
  • 3.每个进程都有自己的页表,进程一多,光是页表占用的空间就很可观了。
  • 4.不要一次性地将全部页表项建好,需要时动态创建页表项。

无论是几级页表,标准页的尺寸都是 4KB,所以 4GB 线性地址空间最多有 1M 个标准页。一级页表是将这 1M 个标准页放置到一张页表中,二级页表是将这 1M 个标准页平均放置 1K 个页表中,每个页表中包含有 1K 个页表项。页表项是 4 字节大小,页表包含 1K 个页表项,故页表大小为4KB,这恰恰是一个标准页的大小。

页目录表来存储这些页表,每个页表的物理地址在页目录表中都以页目录项(Page Directory Entry, PDE)的形式存储,页目录项大小同页表项一样,都用来描述一个物理页的物理地址,其大小都是 4 字节,而且最多有 1024 个页表,所以页目录表也是 4KB 大小,同样也是标准页的大小。

页目录表中共 1024 个页目录项,一个页目录项中记录一个页表物理页地址,页大小都是 0x1000,即 4096,每个页表中有 1024 个页表项,每个页表项中是一个物理页地址,最终数据写在这页表项中指定的物理页中。

二级页表是如何工作的 ?

二级页表地址转换原理是将 32 位虚拟地址拆分成高 10 位、中间 10 位、低 12 位三部分,

  • 高 10 位作为页表的索引,用于在页目录表(PT)中定位一个页目录项(PDE),页目录项中有页表物理地址,也就是定位到了某个页表。
  • 中间 10 位作为物理页的索引,用于在页表内定位到某个页表项(PTE),页表项中有分配的物理页地址,也就是定位到了某个物理页。
  • 低 12 位作为页内偏移量用于在已经定位到的物理页内寻址。

访问任何页表内的数据都要通过物理地址,由于页目录项 PDE 和页表项 PTE 都是 4 字节大小,给出了 PDE 和 PTE 索引后,还需要乘以 4,再加上页表物理地址,这才是最终要访问的绝对物理地址。

  • 具体流程如下
  • 1.用虚拟地址的高 10 位乘以 4,作为页目录表内的偏移地址,加上页目录表的物理地址(CR3内的基地址),所得的和,便是页目录项的物理地址。读取该页目录项,从中获取到页表的物理地址。
  • 2.用虚拟地址的中间 10 位乘以 4,作为页表内的偏移地址,加上在第 1 步中得到的页表物理地址,所得的和,便是页表项的物理地址。读取该页表项,从中获取到分配的物理页地址。
  • 3.虚拟地址的高 10 位和中间 10 位分别是 PDE 和 PTE 的索引值,所以它们需要乘以 4,而低 12 位作为页内偏移直接与第2步物理页地址相加,即可得到实际内存物理地址。

111

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章