关于虚拟内存实现你需要知道的事情(基于 xv6)

956 阅读4分钟

image.png

关于虚拟内存实现--你需要了解的事情(基于 xv6)

预备知识

  • 了解操作系统分页的基本实现

使用的英文缩写

  • 页目录索引: pte(page table entry)
  • 虚拟地址: va
  • 物理地址: pa
  • 物理页号: ppn(Physical Page Number)

introduction

  • 通过例题讲解分页的基本原理, 便于平缓过渡到 xv6 代码实现
  • 借用 xv6 讲解分页的实现(仅讲解核心代码, 不了解 xv6 对阅读无影响) 做到知行合一

二级分页例题

  • 页表大小: 32 bytes
  • 物理内存: 128 page
page   0:1b1d05051d0b19001e00121c1909190c0f0b0a1218151700100a061c06050514
...
page  33:7f7f7f7f7f7f7f7fb57f9d7f7f7f7f7f7f7f7f7f7f7f7f7f7f7ff6b17f7f7f7f

page  53:0f0c18090e121c0f081713071c1e191b09161b150e030d121c1d0e1a08181100

page 108:83fee0da7fd47febbe9ed5ade4ac90d692d8c1f89fe1ede9a1e8c7c2a9d1dbff
...
page 127:7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7fdf7f7f7f7f7f7f7f7f7f7f7f7f957f7f

PDBR: 108 (decimal) [This means the page directory is held in this page]

求虚拟地址(va) 0x611c 对应的物理地址并求出该物理地址中存放的值是多少?

提示: va 以及 pte 结构如下图(看一下自己是否可以推导出来)

image.png

image.png

解析

  1. 首先定位到二级目录的位置: PDBR: 108, 结合 va 前五位(pte 偏移量), 得下一级目录在 page33
pde index:0x18 [decimal 24] pde contents:0xa1 (valid 1, pfn 0x21 [decimal 33])
  1. 按照 1. 的逻辑, 定位出物理地址的 page(page 33 结合 va 中间 5 位)
pte index:0x8 [decimal 8] pte contents:0xb5 (valid 1, pfn 0x35 [decimal 53])
  1. 物理 page 结合物理地址偏移(va 后 5 位), 即可得出物理地址中存放的值
Translates to Physical Address 0x6bc --> Value: 0x08

上述过程图解如下

image.png

核心的逻辑在于: pte 中只存储相应物理内存的页号, va 中只存储相对应的偏移量

xv6 分页代码讲解

riscv 中 va, pa, pte page 的结构

riscv 页表为三极结构

  • 页大小: 4kb
  • va: 64 位, 低 offset 12 位; 三级目录索引, 每级各 9 位; 剩下高位与分页无关
  • pa: 64 位, 低 offset 12 位; FFN 44 位; 剩下高位与分页无关
  • pte: 64 位, 低 权限位 10 位; FFN 44 位; 剩下高位与分页无关 具体结构如下图 image.png

千呼万唤始出来-xv6 中分页的建立

创建分页流程

graph TD
创建二级pagetable --> 循环创建一级以及0级pagetable --> 将其pte填入上一级的pagetable中 --> 最后将物理地址填入上一级的pagetable中 

创建二级pagetable

调用kalloc创建二级目录作为根目录(8 * 512 = 4096 bytes)并初始化其内存单元(全为0)

kpgtbl = (pagetable_t) kalloc();
memset(pagetable, 0, PGSIZE);

调用mappages进行va与pa的map(kernel/vm.c)

mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)

  1. 获取 va, va + size - 1(va 最后一个内存单元) 的页表号(去掉 offset), c a = PGROUNDDOWN(va); last = PGROUNDDOWN(va + size - 1);

  2. 调用walk创建 va->pa 的映射(注意: 这里只创建映射, 不负责分配 pa 的内存空间), 返回 0 级页表的 pte

  3. 对于 pte 进行相关校验处理

  4. 将 pa->pte 并添加状态位, 存储在 pte 中 c *pte = PA2PTE(pa) | perm | PTE_V;

  5. 该页表映射处理完毕, 判断是否需要继续映射(size > PAGSIZE) C if(a == last) break; a += PGSIZE; pa += PGSIZE;

调用walk 函数创建多级目录

循环创建多级目录(结合 va 偏移), 这里以 level = 2 为例

  1. 宏 px: 获得二级 pte 偏移量
  2. 求出 1 级 pte 在二级页表中的物理地址
  3. 查看该 pte 是否在二级页表中, 由于这是开始创建页表映射, 故一定不在, 那么创建一级页表
  4. 将新创建出的 pagetable(注意这里创建出来的是一整张页表, 故其返回的物理地址偏移量一定为 0) 物理地址进行映射为 pte, 放入上一级页表中(8 字节)
  5. 最后将实际的物理地址页表号放入 0 级页表中

至此完成了映射