Lab3

页表实验。

这里我觉得主要关注的就是页表的初始化以及页表如何工作,涉及的函数较多,也比较复杂。

初始化流程

下面是 kernel/main.c 的 main() 代码。

初始化的流程是:

kree() 将所有物理空间初始化为一页一页,并将底部地址加入 freelist ->

kvminit() 初始化kernel_pgtbl ->

kvminithart() 初始化 satp寄存器,在riscv中satp存储一级页表的位置 ->

procinit() 初始化每一个进程的内核栈

void
main()
{
  if(cpuid() == 0){
    consoleinit();
#if defined(LAB_PGTBL) || defined(LAB_LOCK)
    statsinit();
#endif
    printfinit();
    printf("\n");
    printf("xv6 kernel is booting\n");
    printf("\n");
    kinit();         // physical page allocator
    kvminit();       // create kernel page table
    kvminithart();   // turn on paging
    procinit();      // process table
    trapinit();      // trap vectors
    trapinithart();  // install kernel trap vector
    plicinit();      // set up interrupt controller
    plicinithart();  // ask PLIC for device interrupts
    binit();         // buffer cache
    iinit();         // inode cache
    fileinit();      // file table
    virtio_disk_init(); // emulated hard disk
#ifdef LAB_NET
    pci_init();
    sockinit();
#endif    
    userinit();      // first user process
    __sync_synchronize();
    started = 1;
  } else {
    while(started == 0)
      ;
    __sync_synchronize();
    printf("hart %d starting\n", cpuid());
    kvminithart();    // turn on paging
    trapinithart();   // install kernel trap vector
    plicinithart();   // ask PLIC for device interrupts
  }

  scheduler();        
}

kvminit() 内核页表

kvminit() 首先通过kalloc() 为 内核页表分配一页的空间 并初始化为 *kernel_pagetable = 0.

kvmmap() 其实就是调用了一个 mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm).

mappages()的功能就是将 va虚拟地址 映射到 pa物理地址。需要注意的是,此时xv6还没开启地址翻译,因此这几个 kvmmap() 传入的参数映射都是直接映射,也就是说 va是多少,pa就是多少。

比较有趣的就是

KERNBASE:0x80000000

Trampoline: Va位于 MaxVa - PGSIZE

procinit() 进程内核栈

值得注意的是 KSTACK()返回值每次间隔两页,因为有一页作为 kstack 的空间,有一页用在Guard Page(利用页错误防止溢出导致其他页被污染,该页不会映射到物理空间,因此不会造成浪费)

每一个用户进程都有一个对应的kernel stack,在xv6里是最多 64个进程,procinit() 为每一个进程都初始化好了 kstack 。

初始化完要重新将新的内核页表存入satp寄存器。

这里就有一个疑问,就是 kernel_pagetable 里面存着每个进程的 kstack 有什么用呢?

kstack是进程的内核栈

进程栈分为用户栈和进程栈

在 user mode 下运行时使用用户栈,在 superior mode 下运行时使用内核栈

那么既然设置了satp寄存器,就意味着地址翻译开始了,那每个进程的首级页表存在哪里呢?

在proc 结构体中:

用户进程创建

用户进程的页表是如何创建的?

通过调用 proc_pagetable() 函数,创建一个用户页表

uvmcreate() 给用户页表分配了物理空间

然后在va顶部映射 trampoline,在trampoline底下映射trapfame

Last updated