Loading

页表 —— MIT 6S081 FALL 2020

内存隔离

应用程序若想被CPU运行,那么它必须被加载到内存中。

内存中有很多程序,如操作系统的内核、cat程序、shell等。操作系统的一个目标就是获得良好的隔离性,即若一个程序中的错误(或故意的恶意代码)不会影响到操作系统的正常运行,显然在内存中需要这种隔离,因为所有程序都被加载到这里。

如上图,假定这是操作系统在某一时刻的内存映像,shell程序占用内存的1000~2000地址,此时若cat程序中有一个错误,导致它向地址1000写入一些数据,若没有隔离机制的话,shell中的内存会被覆盖。cat的错误影响了操作系统的正常运行。

地址空间

一种隔离的思想就是,给每个程序分配单独的地址空间,它是从0开始到某个上限n的一段虚拟空间。

这样如果cat还想向1000写入数据,它写入到的是自己的地址空间,这不会影响到shell的正常运行。cat只能访问它自己的地址空间。

隔离形成了,但接下来的问题是如何将这种每个程序的地址空间映射到实际内存中的物理空间。这需要一些硬件支持,当CPU发起一个包含地址的命令时,比如将7保存到地址1000,这时的地址1000是一个虚拟地址,虚拟地址会被传递到内存管理单元(MMU)中,MMU解析这个虚拟地址,得到实际的物理地址,然后再通过这个物理地址访问内存。

通常MMU中不保存虚拟地址到物理地址的映射表,这个表一般保存在内存中,每一个进程都有一个独立的表。CPU中会有一些寄存器来保存当前进程的表在内存的哪个部分,在xv6中,这个寄存器被称作satp。当切换进程时,一个工作就是切换satp寄存器中的值以保证该进程虚拟地址空间到物理地址的正常转换。

页表

如果在地址映射表中把地址空间的每一个地址都映射到物理地址,那光是这张表,内存就已经无法容纳,64位处理器可以表示的地址有2^64个。

解决办法就是不对直接地址空间中的地址进行映射,而是抽象出一个页面(page),对每个页面来映射。一个页面通常是4KB,也就是4096字节。

在RISC-V中,64位的虚拟地址被分割成25位的用于扩展的未用地址,27位用于索引页面的地址,12位页面中的偏移量。27位用于索引页面的地址意味着内存中的映射表的大小从2^64下降到2^27,也就是映射表中可以有2^27个条目,每个页面为4096字节,后面的12位偏移量正好代表该数据在页面中的位置,因为2^12 = 4096

当MMU转换时,它使用27位的索引在页表(也就是之前所说的映射表,使用页面之后我们称它页表)中获得一个条目,这个条目中保存了44位的物理页号(PPN)和10位的FLAG标志位,MMU可以通过这44位的物理页号来获得内存中一个4096字节的物理页,然后通过之前12位的偏移量获得要找的数据在这个页面的位置。所以44位的PPN+12位的offset就是物理地址,这样就完成了虚拟地址空间到物理地址空间的转换。

虚拟地址空间实际上有用的位数是27+12,所以最多包含2^39大小的虚拟空间,也就是512GB。而图中的物理地址空间实际上是56位,所以物理地址空间可以比虚拟地址空间大。

上面所述还不是RISC-V中的最终实现,因为即使是2^27的虚拟地址空间,也不是一个小数目,每个进程都需要维护这样一张映射表,很快就会耗尽内存。

解决办法是分级索引,嘶,学习数据库原理和操作系统原理之后发现,一遇到这种状况就是分级哈哈哈。

如下图,虚拟地址中的27位的索引地址被分为三份,每份9位,先看第一个9位,它是一个顶层索引目录,2^9=512,所以它其中有512个条目,每个条目是64位,也就是8字节,所以一个索引目录占用4096的大小,也就是一个页面,这不算大。

当你在顶层索引目录中得到一个索引项时,这个索引项中的PPN实际指向一个下一级索引目录,我们需要使用虚拟地址索引中的下9位来对这个索引目录进行索引,同样,得到下一级索引目录的地址,最终第三级得到的才是物理地址。

按照这种层级关系,我们便可以实现虚拟地址空间表的动态分配,当程序不需要那么大的内存时,我们便只需要分配几个索引目录即可。这样,增加了索引次数,但是我们获得了更少的空间浪费,如果地址空间中的大部分空间没有使用,我们不需要为它们占用内存。

这样我们最少情况下只需要一个4KB的顶层目录,4KB的中间目录,4KB的第三级目录,我们只需要12KB的内存。

当切换进程时,satp寄存器需要保存指向该进程的顶层页目录。

那么如何解决对于解析任何地址都需要三次内存访问操作的问题?

就是使用一个TLB缓存(Translation Look-aside Buffer),TLB中存储最近使用的虚拟地址到物理地址的映射。它是CPU旁边的一个东西,一般来说操作系统不知道它的存在。

我们要知道所有上面这些操作都在硬件中完成,操作系统并不知道。

但是,操作系统可以控制页表,比如当一个指令要访问的页面无效的时候产生页面错误,更新页表,再重新执行该指令。

posted @ 2022-02-17 11:07  yudoge  阅读(101)  评论(0编辑  收藏  举报