MIT6.828-Lab2-Part2-虚拟内存

 

虚拟内存

Virtual, Linear and Physical Addresses

在x86体系中

  1. 一个虚拟地址由段选择子(segment selector)和段内偏移(segment offset)组成
  2. 一个线性地址由段地址转换机构(segment translation)将虚拟地址进行转换后得到
  3. 一个物理地址由分页地址转换机构(page translation)将线性地址转换后得到,这个地址最终会送到地址总线上
           Selector  +--------------+         +-----------+
          ---------->|              |         |           |
                     | Segmentation |         |  Paging   |
Software             |              |-------->|           |---------->  RAM
            Offset   |  Mechanism   |         | Mechanism |
          ---------->|              |         |           |
                     +--------------+         +-----------+
            Virtual                   Linear                Physical

Exercise2:

  1. 阅读Intel 80386 Reference Manual的5.6两章

C 指针是虚拟地址的“偏移”部分。在 boot/boot.S 中,通过全局描述符表 (GDT)将所有段基地址设置为 0 并设定界限为 0xffffffff 来有效地禁用段转换。因此 segment selector 没有作用,线性地址总是等于虚拟地址的偏移量。在lab2只需要关注 Page translation

在lab1的part3中,将从 0x0010|0000 的 4MB 物理内存映射到了 0xf010|0000 开始的虚拟地址,在这次实验会将物理空间的头256MB内存映射到(从 0xf000|0000 开始的虚拟地址上 和 一些其他的虚拟内存空间)。

Exercise3:

  1. QEMU monitor commands 页面熟悉 qemu 的操作命令
    1. xp/Nx paddr: 查看物理地址 paddr 处保存连续的 N个 words
    2. info registers: 查看寄存器
    3. info mem: 查看映射的虚拟内存地址和它们的访问权限
    4. info pg: 查看当前的页表结构:虚拟地址范围;区分是页目录条目(PDE)还是页表条目(PTE);访问权限;物理地址范围

一旦进入了保护模式(which we entered first thing in boot/boot.S),所以对内存的引用都会被视作虚拟地址,然后由MMU进行翻译。所以所有C指针都是虚拟地址。

JOS 内核经常需要将地址按照一种模糊的值或整数值来进行操作,而不是进行解引用(dereference)。比如MMU,肯定会涉及对VA(virtual address)和PA(physical address)的操作,那么应该如何区分呢?JOS源码使用 uintptr_t, phyaddr_t 分别代表VA,PA。但它们实际上都是 uint32_t,所以是可以互相赋值的。

可以通过强制类型转换(casting)来将 uintptr_t 转换为指针然后解引用,虽然 `phyaddr_t` 也可以这么操作,但是解引用的地址并不是真实的物理地址指向的内存此时该物理地址被当作虚拟地址,因此是不合理的操作。

所以可以直接回答lab2的question1: x 的类型是 uintptr_t


JOS的内核有时候需要读写内存,但是却只知道物理地址。但是内核并不能直接操作物理地址,因此需要将此物理地址转换为虚拟地址才行。比如JOS将从0x0开始的物理地址映射到了从0xf000 0000开始的虚拟地址,那么内核只需要将目前已知的物理地址+0xf000 0000就可以找到对应的虚拟地址,可以使用 KADDR(pa) 来完成这个加法操作。

反之由VA得到PA的操作可以由 PADDR(va) 来完成。

Reference counting

之后的lab经常会碰到多个VA映射到同一个物理页(physical page)的情况,需要跟踪此物理页对应的 struct PageInfopp_ref 字段,统计它被引用的次数。当 pp_ref = 0 时,就说明它可以被释放。通常来说,任意一个物理页p的pp_ref值等于它在所有的页表项中,被位于虚拟地址UTOP之下的虚拟页所映射的次数(UTOP之上的地址范围在启动的时候已经被映射完成了,之后不会被改动)。

当使用 page_alloc() 时需要注意它返回的 struct PageInfopp_ref 等于 0,因此如果对这个返回的页面做了任何事情,需要立马执行: pp_ref + 1

Page Table Management

着手开始编写管理页表的程序:包括插入和删除线性地址到物理地址的映射关系,以及创建页表等操作。

Exercise4:

  1. kern/pmap.c 中,实现如下函数的代码:
    1. pgdir_walk()
    2. boot_map_region()
    3. page_lookup()
    4. page_remove()
    5. page_insert()
  2. 之后 mem_init() 中的 check_page() 会测试这些页表管理程序

参考资料

  1. cnblog-fatsheep9146