前言:
有了前两篇的基础,xv6已经具有了管理物理内存的能力,可以主动申请和释放物理页,这次的主题是“虚拟内存”。
一.总览
xv6为每个进程维护一个页表,维护每个进程的用户地址空间的映射,外加一个页表维护内核地址空间的映射。
而内核可以配置它自己的地址空间布局,用虚拟地址访问所有的Memory资源和I/O资源,下图展示了内核虚拟地址空间的布局以及到物理地址的映射。

在右边的物理地址空间中,由低地址到高地址分别是boot ROM,CLINT(负责控制RISC-V的软件中断和定时器中断),PLIC(对外部中断进行仲裁和分发),UART0,VIRTIO disk和其他I/O设备,CPU和设备的交互本质上是通过读写这些设备寄存器进行的。从(KERNBASE)到0X88000000(PHYSTOP)共128MB空间是RAM空间。
左边的0~MAXVA(−1=0x3fffffffff)是内核的虚拟地址空间范围,RV64最常用的分页是Sv39,而xv6只用了其中的38bit,所以MAXVA是上面的值。
从图中可以知道内核是通过“直接映射”的方式来访问Memory和I/O的,例如内核本身在虚拟地址空间和物理地址空间中都处于相同的位置0X80000000(KERNBASE)。
但是还有一些虚拟地址空间不是通过直接映射的方式映射到物理页面上的:
(1)trampoline page,这个页面在内核虚拟地址空间的最顶部,关于它的作用之后会有章节专门进行,但我们可以发现一个有趣的事实:假设一个物理页面持有trampoline的代码,那么这个物理页面被映射了两次!一次是在内核虚拟地址空间的顶部,一次是在对应的直接映射区域,不是吗?
(2)kernel stack pages,每个进程有自己的内核栈;除此之外还有一个Guard page,有Guard page的好处是如果内核栈溢出了会产生异常,方便提示内核有一些糟糕的事情发生了;没有内核栈时一个进程的内核栈溢出会覆盖其他进程的内核栈,导致错误的结果。
此外我们可以看到在映射trampoline page和内核代码时给他们的权限是R(读)和X(执行),内核会从这些页面读取并执行指令;在映射其他页面时给他们的权限是R(读)和W(写),所以内核可以在这些页面上读和写。
接下来就是将上述过程用代码实现。
二.代码分析
1.main.c
// start() jumps here in supervisor mode on all CPUs.
void
main()
{
if(cpuid() == 0){
consoleinit();
printfinit();
printf("\n");
printf("xv6 kernel is booting\n");
printf("\n");
kinit(); // physical page allocator
kvminit(); // create ker

最低0.47元/天 解锁文章
8370

被折叠的 条评论
为什么被折叠?



