🗒️Xv6-VM

2023-4-25|2023-5-22
Anthony
Anthony
type
status
date
slug
summary
tags
category
icon
password
本Blog基于任职于美国波特兰洲立大学的教授@hhp3
在Youtube上对xv6内核的讲解视频
本期讲解xv6中的初始空间分配,分成了介绍和代码两个部分
本文直接将这两部分合起来,介绍函数作用的同时,也讲解函数的具体实现。
视频链接在下面
 

前置知识

Xv-6 使用一个三级页表结构,详细的介绍可以看
🗒️
Xv6-Page Table
,在链接中还详细介绍了walk函数在遍历页表的作用
Xv6使用的三级页表结构
Xv6使用的三级页表结构
 
这是在xv6中从虚拟地址到物理地址的映射表,非常重要!!!
xv6-virtual address - physical address
xv6-virtual address - physical address
 
注意内核空间的虚拟地址是映射到free memory 所对应的物理地址的,并不是向上图所指的那样,正确应该为
notion image
 
 
内核空间的进程空间为
notion image
 

代码讲解

walk()

遍历三级页表🌳,如果alloc为1,则代表需要创建页表
接收参数:
pagetable → 指向页表数根节点的指针
va → 虚拟地址
alloc → 布尔值,0,1
返回值:
返回最底层PTE(page table entry)的地址
💡
walk 函数的详解在
🗒️
Xv6-Page Table
 

mappages()

该函数首先接收一个页表的指针,一个虚拟地址,一个页面大小,一个物理地址,一个权限参数
该函数首先判断size是否合法,然后获取到要映射的空间的开始和结尾,然后分配若干张页表
该函数根据虚拟地址和需要创建的大小来决定需要被添加到虚拟地址空间的页表数
 

一步步解释

该函数首先确保创建的页表大小不是0,然后PAGEROUNDDOWN 将虚拟地址和页表空间对齐,a是这个页表空间的开头,last是这个页表空间的结尾。换言之,这就是不管传入的具体size是多少,分配的单位都是以一个页面为单位的。
 
接下来循环体调用walk()分配空间,PTE_V是页表的有效位,这时的有效位置不应该是1。接下来生成一个pte的地址。通过不断的将虚拟地址开头增加一个PGSIZE,将物理地址的开头也增加一个PGSIZE,完成若干页表的创建。
 

kvmmap()

该函数是一个包装函数,仅仅是调用mappages()
 

kvmmake()

该函数用于创建和初始化内核页表,该函数会调用kvmmap()
  • 添加虚存和物理内存的实际映射(直接映射),
  • 添加I/O设备的映射(直接映射)
  • 将TRAMPOLINE page 映射到最高页
  • 调用proc-mapstacks()proc-mapstacks()调用kalloc()函数给每一个进程分配一个内核的堆栈(当进程在Kernel mode执行时使用)
 
 

一步步解释

首先使用kalloc创建一张页表,该页表作为顶层页表,将会作为最后的返回参数
 
然后
创建uart(universal asynchronous receive transmit unit)映射,将该页赋予可读可写权限。我们可以看到虚拟地址和物理地址是一样的(第二个和第三个参数),页面大小是一个页表的大小。
创建磁盘映射,和uart参数一样
创建PLIC(platform level interrupt controller)大小是4MB
notion image
 
接下来
创建代码映射,可读,可执行,不可写。
创建内核的数据区,其中free memory部分就是kalloc和kfree可以使用的
我们可以根据下图辅助理解创建空间的位置以及映射方式
notion image
 
然后映射trampoline page,注意这不是一个直接映射。
 
最后我们调用proc_mapstacks()为每一个kernel进程创建映射。
 

proc_mapstacks()

该函数接收一个内核页表的地址,遍历64个进程,对于每一个进程都调用kalloc()分配一个页表,调用KSTACK() 获取该进程的虚拟地址,然后映射到物理地址去。
 
 
 
 
 
 
 
 
 
Xv6-SchedulingXv6-Lock