为什么要使用虚拟内存
有助于实现隔离性

虚拟内存的概念

给包括内核在内的所有程序专属的地址空间。所以,当我们运行 cat 时,它的地址空间从 0 到某个地址结束。当我们运行 Shell 时,它的地址也从 0 开始到某个地址结束。内核的地址空间也从 0 开始到某个地址结束。

虚拟内存可以比物理内存更大,也可以比物理内存更小

页表(Page Tables)

什么是页表

页表是在硬件中通过处理器和内存管理单元(Memory Management Unit)实现,页表顾名思义为一个表,其保存了虚拟内存和物理内存的映射关系,页表自身也保存在内存中,因此 cpu 中会有一个专门的寄存器保存页表的内存地址

页表是怎么工作的

** 页表中不会为每个地址记录一个条目,这会导致页表体积非常大**
页表中保存的是页表的地址

index 和 offset

页表中被分为两个部分,一部分被称为 index,一部分被称为 offset,index 用来确定是哪个 page,offset 用来确定 page 中的哪一个字节

假设 offset 是 12,那么 page 中的第 12 个字节被使用了。将 offset 加上 page 的起始地址,就可以得到物理内存地址。

虚拟内存地址都是 64bit,这也说的通,因为 RISC-V 的寄存器是 64bit 的。但是实际上,在我们使用的 RSIC-V 处理器上,并不是所有的 64bit 都被使用了,也就是说高 25bit 并没有被使用。这样的结果是限制了虚拟内存地址的数量,虚拟内存地址的数量现在只有 2^39 个,大概是 512GB。当然,如果必要的话,最新的处理器或许可以支持更大的地址空间,只需要将未使用的 25bit 拿出来做为虚拟内存地址的一部分即可。
在剩下的 39bit 中,有 27bit 被用来当做 index,12bit 被用来当做 offset。offset 必须是 12bit,因为对应了一个 page 的 4096 个字节。

物理内存地址是 56bit,其中 44bit 是物理 page 号(PPN,Physical Page Number),剩下 12bit 是 offset 完全继承自虚拟内存地址(也就是地址转换时,只需要将虚拟内存中的 27bit 翻译成物理内存中的 44bit 的 page 号,剩下的 12bitoffset 直接拷贝过来即可)。

每个进程都拥有自己的一个页表

index 细分

如果每个进程都拥有一个自己的页表,那么物理内存会被页表耗尽的。
因此,将 index 的 27 位分成 3 个 9 位,分级索引
前 9 个 bit 被用来索引最高级的 page directory
一个 directory 是 4096Bytes,就跟 page 的大小是一样的。Directory 中的一个条目被称为 PTE(Page Table Entry)是 64bits,就像寄存器的大小一样,也就是 8Bytes。所以一个 Directory page 有 512 个条目。
所以实际上,SATP 寄存器会指向最高一级的 page directory 的物理内存地址,之后我们用虚拟内存中 index 的高 9bit 用来索引最高一级的 page directory,这样我们就能得到一个 PPN,也就是物理 page 号。这个 PPN 指向了中间级的 page directory。
当我们在使用中间级的 page directory 时,我们通过虚拟内存地址中的 L1 部分完成索引。接下来会走到最低级的 page directory,我们通过虚拟内存地址中的 L0 部分完成索引。在最低级的 page directory 中,我们可以得到对应于虚拟内存地址的物理内存地址。