Lab3 page tables

  1. Print a page table
    添加一个打印页表的内核函数,参考递归释放页表的函数freewalk(),
    vm.c
static char * prefix[3] = {"..", ".. ..", ".. .. .."};
int
pgtblprint(pagetable_t pagetable, int level) {
  if (level > 2) {
    return 0;
  }

  for (int i = 0; i < 512; i++) {
    pte_t pte = pagetable[i];
    if (pte & PTE_V) {
      uint64 pa = PTE2PA(pte);
      printf("%s%d: pte %p pa %p\n", prefix[level], i, pte, pa);

      if ((pte & (PTE_R|PTE_W|PTE_X)) == 0) {
        uint64 child = PTE2PA(pte);
        pgtblprint((pagetable_t)child, level + 1);
      }
    }
  }

  return 0;
}

int
vmprint(pagetable_t pagetable) {
  printf("page table %p\n", pagetable);
  return pgtblprint(pagetable, 0);
}

defs.h

int             vmprint(pagetable_t pagetable);

exec.c

vmprint(p->pagetable);

测试结果


pte-2.PNG
  1. A kernel page table per process
    每个进程进入内核时,都拥有自己的内核页表
    proc.h
    进程结构体struct proc结构体增加内核页表字段
pagetable_t kernelpagetable; // kernel page table

vm.c
增加proc_kvminit函数,创建页表,并修改kvminit

void
kvminit()
{
  kernel_pagetable = proc_kvminit();
  
  // CLINT
  uvmmap(kernel_pagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
}

pagetable_t
proc_kvminit()
{
  pagetable_t kernelpagetable = uvmcreate();
  if (kernelpagetable == 0) {
    return 0;
  }

  uvmmap(kernelpagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);
  uvmmap(kernelpagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
  // uvmmap(kernelpagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
  uvmmap(kernelpagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
  uvmmap(kernelpagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
  uvmmap(kernelpagetable, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
  uvmmap(kernelpagetable, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
  return kernelpagetable;
}

void uvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
{
  if (mappages(pagetable, va, sz, pa, perm)) {
    panic("uvmmap");
  }
}

创建进程的时候,为进程分配独立的页表,以及内核栈
proc.c

p->kernelpagetable = proc_kvminit();
  char *pa = kalloc();
  if (pa == 0) {
    panic("kalloc");
  }
  uint64 va = TRAMPOLINE - 2*PGSIZE;
  uvmmap(p->kernelpagetable, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
  p->kstack = va;

在调度器调度进程时,切换到该进程对应的内核页表
proc.c

// 切换到进程独立的内核页表
        w_satp(MAKE_SATP(p->kernelpagetable));
        sfence_vma();

        swtch(&c->context, &p->context);

        // 切换到全局内核页表
        kvminithart();

进程结束时,释放进程独享的页表以及内核栈
proc.c

// 释放进程的内核栈
  if (p->kstack) {
    pte_t* pte = walk(p->kernelpagetable, p->kstack, 0);
    if (pte == 0) {
      panic("freeproc: walk");
    }
    kfree((void*)PTE2PA(*pte));
  }
  p->kstack = 0;

  if (p->kernelpagetable) {
    kvm_free_kernelpagetable(p->kernelpagetable);
  }

vm.c

void 
kvm_free_kernelpagetable(pagetable_t pagetable)
{
  for (int i = 0; i < 512; i++) {
    pte_t pte = pagetable[i];
    if ((pte & PTE_V)) {
      pagetable[i] = 0;
      if ((pte & (PTE_R|PTE_W|PTE_X)) == 0) {
        uint64 child = PTE2PA(pte);
        kvm_free_kernelpagetable((pagetable_t)child);
      }
    }
  }
  kfree((void*)pagetable);
}

测试结果:


usertest1.PNG
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 为什么需要虚拟内存 shell进程由于bug,引发了随机写入某些内存地址,这些内存地址可能是其他进程使用的,而可能...
    西部小笼包阅读 324评论 0 0
  • 操作系统的隔离性 如果没有操作系统,应用程序会直接与硬件交互。比如,应用程序可以直接看到CPU的多个核,看到磁盘,...
    西部小笼包阅读 314评论 0 0
  • 实验三:虚拟内存管理 专业班级: 学号: 姓名: 上课老师: 一、实验目的 1.了解虚拟内存的Page Fault...
    北北南北阅读 2,068评论 0 2
  • 页表是操作系统给每个进程提供私有地址空间和内存的一种机制。页表决定内存地址的意义以及哪些物理内存能够访问。他们允许...
    sarto阅读 2,854评论 0 0
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,237评论 2 33