XV6的内存管理
简介
用自己的语言描述XV6的内存管理系统。
引子
当xv6在启动的过程中,主控函数main
调用了三个函数来完成内存管理系统的初始化。
kinit
把空闲的物理内存以页为单位放到一个单向链表kmem.freelist
里,这样就可以通过操作这个链表来管理物理页了。kvminit
是给物理页和虚拟页之间建立映射关系,这样就为开启分页做好了准备。kvminithart
用于开启分页。
与内存管理相关的文件有kernel
目录下kalloc.c
、vm.c
、memlayout.h
。
kalloc.c
的作用是进行物理内存管理,它的核心数据结构是kmem.freelist
,它的核心函数是kfree
和kalloc
。vm.c
的作用是进行虚拟内存的管理,它的核心变量是kernel_pagetable
,它的函数用于管理虚拟内存,即包括在管理员模式下,也包括在用户模式下。memlayout.h
的作用是描述物理内存的布局。其内容是一些重要的物理地址的宏定义。
虚拟内存到物理内存的映射
RISC-V有三种映射方式,Sv32、Sv39、Sv48。它们区别仅在于映射的层级不一样,进而物理地址和虚拟地址的可表示范围不一样。其中Sv32是二级映射,Sv39是三级映射,Sv48是四级映射。
虚拟地址和物理地址的页内偏移是相等的,所以,它们的大小的设计仅需考虑PTE。
对于Sv32,每个PTE占用4个字节,所以页表对应了(4K/4B)=1024个PTE,所以每一级VPN需要占用10位,所以它的虚拟地址是10*2+12=32位。由于高22位是PPN,所以物理地址是22+12=34位。
对于Sv39和Sv48,每个PTE占用8个字节,所以页表对应了(4K/8B)=512个PTE,所以每一级VPN需要占用9位。类似于Sv32的计算方式,可得它们的有效虚拟地址,Sv39是9*3+12=39位,Sv48是9*4+12=48位。它们的PPN都是44位,所以物理地址是44+12=56位。
内核空间的映射
对于机器virtio来说,物理内存是从0x80000000开始计数的,物理内存前面的部分是MMIO。
0~0x7FFFFFFF是MMIO,映射在虚拟内存相同的位置。
0x80000000~0x80000000+128M,映射在虚拟内存相同的位置。
trampoline页,用于内核空间和用户空间的切换,被映射在虚拟内存的顶部。
内核栈,被映射在虚拟内存里trampoline页的下面。
参考文档
- xv6-riscv-book Chapter 3
- riscv-privileged-v1.10