Lec 4,5 - Lab 3 page table

2023-11-05  本文已影响0人  西部小笼包

为什么需要虚拟内存

shell进程由于bug,引发了随机写入某些内存地址,这些内存地址可能是其他进程使用的,而可能影响内核或其他进程的执行。

所以我们需要引入虚拟内存来实现隔离的进程的内存地址空间。每个进程拥有自己的内存地址空间,它们能够读写自己的内存,但不能访问其他进程的内存。实现多个地址空间与物理内存之间多路复用的挑战在于如何保持隔离性。

页表(page table)

xv6操作系统使用RISC-V架构的分页硬件来实现地址空间(AS)。
页表提供了一个间接层来处理地址映射,CPU通过内存管理单元(MMU)映射到物理内存(RAM)。内核告诉MMU如何将每个虚拟地址(VA)映射到物理地址(PA)。


image.png

为了实现不同的地址空间,需要多个页表并在切换过程中更换页表。
MMU有一个satp寄存器,用于内核写入以更改页表。
页表存在于内存中,satp寄存器保存当前页表的物理地址。
MMU并不会保存page table,它只会从内存中读取page table,然后完成翻译。

页表的大小和结构

1699189384090.png

TLB

kernel pagetable

1699190362825.png

右半部分

KERNBASE

图中的右半部分的结构完全由硬件设计者决定。如你们上节课看到的一样,当操作系统启动时,会从地址0x80000000开始运行,这个地址其实也是由硬件设计者决定的。
主板的设计人员决定了,在完成了虚拟到物理地址的翻译之后,如果得到的物理地址大于0x80000000会走向DRAM芯片,如果得到的物理地址低于0x80000000会走向不同的I/O设备。

左半部分

当机器刚刚启动时,还没有可用的page,XV6操作系统会设置好内核使用的虚拟地址空间,也就是这张图左边的地址分布。


1699190587957.png

guard page

有一些page在虚拟内存中的地址很靠后,比如kernel stack在虚拟内存中的地址就很靠后。这是因为在它之下有一个未被映射的Guard page,这个Guard page对应的PTE的Valid 标志位没有设置,这样,如果kernel stack耗尽了,它会溢出到Guard page,但是因为Guard page的PTE中Valid标志位未设置,会导致立即触发page fault,这样的结果好过内存越界之后造成的数据混乱。立即触发一个panic(也就是page fault),你就知道kernel stack出错了。同时我们也又不想浪费物理内存给Guard page,所以Guard page不会映射到任何物理内存,它只是占据了虚拟地址空间的一段靠后的地址。

权限

user pagetable

1699190823363.png

内核与用户空间都映射了跳板页(trampoline page):

跳板页是一种特殊的内存页面,它既存在于用户空间的地址映射中,也存在于内核空间的地址映射中,虽然用户位(U bit)没有被设置。
这样的设置方便了用户空间与内核空间之间的转换。当用户程序需要进行系统调用,即从用户模式切换到内核模式时,通过跳板页可以更加顺畅地进行。因为切换了pagetable之后,理论上同样的VA会用不同的页表找到不同的PA,但是这2块映射是一致的,那么不同的页表,也可以保证代码可以顺畅执行。

内核如何使用用户虚拟地址?

xv6代码结构

Lab 3 Optional challenge

Unmap the first page of a user process so that dereferencing a null pointer will result in a fault. You will have to change user.ld to start the user text segment at, for example, 4096, instead of 0.

这道题目的难点有2,当xv6 启动时,我们开启的第一个进程是initcode。这个进程强制设置了代码的起始点从地址0开始。同时vmprint的test函数也需要当进程号为1的时候,打印含有0地址的3级页表项。
所以这道题,在理解了整个新的进程初始化时页表怎么分配后,我们需要对非1号进程,进行Unmap the first page的操作。
那么理解进程的页表怎么创建就非常重要。除了1号进程,进程的创建分为2种。第一种就是纯fork,第二种就是fork+exec。fork里的操作就是复制父进程的页表到子进程。所以我们保证父进程没有0号地址的页表项。那么子进程也不会复制这个页表项。那么2号进程 shell (exec sh) 其实就作为其他进程的父进程,他的页表需要最先做unmap。 那么当 fork 返回后,如果是接着exec,会进到exec.c的exec中。
这里做的事情是开启一个新的页表,然后把ELF要执行的代码段(就是user space下注册的命令的main函数里的代码)加载进新页表的text 和 data段。随后创建guard page 和 stack page。然后把原来fork出来的pagetable 给free掉,同时把新创建的pagetable用作这个进程的pagetable.
那么核心代码,其实就是,对于1号进程,从ADDRESS 0开始free. 对其他进程的新的pagetable做一次unmap,然后因为FORK出来的oldpagetable,已经不含有第一页了。再free的时候得从第二页开始。具体代码改动如下:

1699196919666.png 1699195074895.png
1699195130318.png

然后uvmcopy, 就得从地址PGSIZE开始,因为0开始的那页已经被unmap了。


1699195307668.png

测试方案

写一个用户命令,nulltest

#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
    char *ptr = (char *)0x0;
    printf("%c\n", *ptr);  // deference null
    exit(0);
}

同时写一个vmprint命令,以及一个vmprint系统调用,可以使得在shell段去打印pagetable来验证是否没有0号PAGE。

#include "kernel/types.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
  if(argc <= 1){
    fprintf(2, "usage: vmprint 0 or 1, which mean abbr or not\n");
    exit(1);
  }
  vmprint(atoi(argv[1]));
  exit(0);
}
1699195856983.png

Add a system call that reports dirty pages (modified pages) using PTE_D.

这道题其实和LAB 3里的第三问的做法差不多,只不过是CHECK PTE_A 变成了 PTE_D; 然后REPORT我的方案是打印所有DIRTY PAGE。

1699196062393.png

测试方案

同样写一个用户端的命令去作为测试。

#include "kernel/param.h"
#include "kernel/fcntl.h"
#include "kernel/types.h"
#include "kernel/riscv.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
  char *buf = malloc(32 * PGSIZE);
  // record init dirty page, check delta
  dirtypages();
  // write two page
  buf[PGSIZE] += 1;
  buf[PGSIZE * 3] += 1;
  dirtypages();
  // read only
  char c = buf[PGSIZE * 4];
  c++;
  dirtypages();
  exit(0);
}
1699196402739.png

符合预期。

Use super-pages to reduce the number of PTEs in page tables.

这一问,我花了非常久的时间,代码改动量非常多。但是也彻底帮我理解vm.c每个函数的作用。因为一旦涉及到了大页的支持,基本这里面每个函数都要修改。
这一问,我直接贴全部代码,小伙伴们可以根据需要自己比对我有哪些改动。
下面我来说一下要实现大页的基本代码思路。

1. kalloc.c

维护2个freelist,一个用来分配普通页,一个用来分配大页。
然后初始化的时候,对不同地址空间进行不同的分工。指定一个地址,低于这个地址为普通页,高于这个地址为大页。
这样做的弊端是大页和普通页没法互相转化。我选择这么做其实也是为了简化问题难度。不然要写的代码会更多。


1699196736942.png
1699196773624.png
1699196831866.png

2. risv.h

1699197059956.png

3. vm.c

原理是当一个page是leaf page,(有PTE_R或者PTE_X) 就不可能是NON-LEAF PAGE。并且当且层级不为2时,就代表它索引到的地址是一个巨页的开始。因为我们不使用额外的一个BIT去表明这个页是否为巨页。这个信息如果外层需要,理论上我们需要在函数签名里一起返回。但是因为我们固定了巨页和普通页的地址分割线。所以我们只需要用一次PTE2PA,然后和SUPSTART比较,就可以知道当前是否为巨页。


1699200566770.png

然后就是根据需求,去改动VM.C里的各种函数,根据是否为巨页,需要写不同的逻辑处理。这里有个注意事项是,要分配巨页的前提条件,除了要求分配的内存大于单个巨页的SIZE,同时也要求需要映射的VA,根据2MB对齐。

要求超级页面按其大小(例如,2 MiB的超级页面需按2MB对齐)进行对齐,是由于MMU在将虚拟地址转换为物理地址和访问内存时的工作方式。

下面是对齐的原因:

如果超级页面的起始虚拟地址没有按2MB边界对齐,这将意味着该地址不能清晰地映射到页面表的单个条目,这违背了使用超级页面进行高效内存管理的目的。它将需要特殊处理以在一个超级页面的末端映射部分页面,并在另一个的开头映射剩余部分,增加了复杂性,并可能抵消性能优势。

// Return the address of the PTE in page table pagetable
// that corresponds to virtual address va.  If alloc!=0,
// create any required page-table pages.
//
// The risc-v Sv39 scheme has three levels of page-table
// pages. A page-table page contains 512 64-bit PTEs.
// A 64-bit virtual address is split into five fields:
//   39..63 -- must be zero.
//   30..38 -- 9 bits of level-2 index.
//   21..29 -- 9 bits of level-1 index.
//   12..20 -- 9 bits of level-0 index.
//    0..11 -- 12 bits of byte offset within the page.
pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{
  if(va >= MAXVA)
    panic("walk");

  int level = 2;
  int end = alloc == SUPPGSIZE ? 1 : 0;  
  for(; level > end; level--) {
    pte_t *pte = &pagetable[PX(level, va)];
    if(*pte & PTE_V) {
      pagetable = (pagetable_t)PTE2PA(*pte);
      if ((*pte & PTE_R) || (*pte & PTE_X)) {
        if (level != 1) panic("walk_level");
        return pte;
      }
    } else {
      if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
        return 0;
      memset(pagetable, 0, PGSIZE);
      *pte = PA2PTE(pagetable) | PTE_V;
    }
  }
  
  if (level > 1 || level < 0) panic("walk_level");
  return &pagetable[PX(level, va)];
}

// Look up a virtual address, return the physical address,
// or 0 if not mapped.
// Can only be used to look up user pages.
uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
  pte_t *pte;
  uint64 pa;

  if(va >= MAXVA)
    return 0;

  pte = walk(pagetable, va, 0);
  if(pte == 0)
    return 0;
  if((*pte & PTE_V) == 0)
    return 0;
  if((*pte & PTE_U) == 0)
    return 0;
  pa = PTE2PA(*pte);
  return pa;
}

// add a mapping to the kernel page table.
// only used when booting.
// does not flush TLB or enable paging.
void
kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm)
{
  if(mappages(kpgtbl, va, sz, pa, perm) != 0)
    panic("kvmmap");
}

// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa.
// va and size MUST be page-aligned.
// Returns 0 on success, -1 if walk() couldn't
// allocate a needed page-table page.
int
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{
  uint64 a, last;
  pte_t *pte;

  int suppg = (size >= SUPPGSIZE) && !(va % SUPPGSIZE);
  int pgsize = suppg ? SUPPGSIZE : PGSIZE;
  if((va % pgsize) != 0)
    panic("mappages: va not aligned");

  if((size % pgsize) != 0)
    panic("mappages: size not aligned");

  if(size == 0)
    panic("mappages: size");
  
  a = va;
  last = va + size - pgsize;
  for(;;){
    if((pte = walk(pagetable, a, pgsize)) == 0)
      return -1;
    if(*pte & PTE_V)
      panic("mappages: remap");
    *pte = PA2PTE(pa) | perm | PTE_V;
    if(a == last)
      break;
    a += pgsize;
    pa += pgsize;
  }
  return 0;
}

// Remove npages of mappings starting from va. va must be
// page-aligned. The mappings must exist.
// Optionally free the physical memory.
void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
  uint64 a;
  pte_t *pte;

  if((va % PGSIZE) != 0)
    panic("uvmunmap: not aligned");
  int pgsize = PGSIZE;  
  for(a = va; a < va + npages*PGSIZE; a += pgsize){
    
    if((pte = walk(pagetable, a, 0)) == 0)
      panic("uvmunmap: walk");
    
    if((*pte & PTE_V) == 0)
      panic("uvmunmap: not mapped");
    if(PTE_FLAGS(*pte) == PTE_V)
      panic("uvmunmap: not a leaf");
    uint64 pa = PTE2PA(*pte);
    int suppg = pa >= SUPSTART;
    pgsize = suppg ? SUPPGSIZE : PGSIZE;  
    if(do_free){
      suppg ? kfree_suppage((void*)pa) : kfree((void*)pa);
    }
    *pte = 0;
  }
}

// create an empty user page table.
// returns 0 if out of memory.
pagetable_t
uvmcreate()
{
  pagetable_t pagetable;
  pagetable = (pagetable_t) kalloc();
  if(pagetable == 0)
    return 0;
  memset(pagetable, 0, PGSIZE);
  return pagetable;
}

// Load the user initcode into address 0 of pagetable,
// for the very first process.
// sz must be less than a page.
void
uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
{
  char *mem;

  if(sz >= PGSIZE)
    panic("uvmfirst: more than a page");
  mem = kalloc();
  memset(mem, 0, PGSIZE);
  mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
  memmove(mem, src, sz);
}

// Allocate PTEs and physical memory to grow process from oldsz to
// newsz, which need not be page aligned.  Returns new size or 0 on error.
uint64
uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
{
  char *mem;
  uint64 a;

  if(newsz < oldsz)
    return oldsz;
  uint64 sz = newsz - oldsz;
  int suppg = sz >= SUPPGSIZE && PGROUNDUP(oldsz) == SUPPGROUNDUP(oldsz);
  int pgsize = suppg ? SUPPGSIZE : PGSIZE;  
  oldsz = PGROUNDUP(oldsz);
  if (suppg) newsz = SUPPGROUNDUP(newsz);
  for(a = oldsz; a < newsz; a += pgsize){
    mem = suppg ? kalloc_suppage() : kalloc();
    if(mem == 0){
      uvmdealloc(pagetable, a, oldsz);
      return 0;
    }
    memset(mem, 0, pgsize);
    if(mappages(pagetable, a, pgsize, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
      suppg ? kfree_suppage(mem) : kfree(mem);
      uvmdealloc(pagetable, a, oldsz);
      return 0;
    }
  }
  return newsz;
}

// Deallocate user pages to bring the process size from oldsz to
// newsz.  oldsz and newsz need not be page-aligned, nor does newsz
// need to be less than oldsz.  oldsz can be larger than the actual
// process size.  Returns the new process size.
uint64
uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
{
  if(newsz >= oldsz)
    return oldsz;

  uint64 sz = oldsz - newsz;
  int suppg = sz >= SUPPGSIZE && PGROUNDUP(newsz) == SUPPGROUNDUP(newsz);;

  if (suppg) {
    if(SUPPGROUNDUP(newsz) < SUPPGROUNDUP(oldsz)){
      int npages = (SUPPGROUNDUP(oldsz) - SUPPGROUNDUP(newsz)) / SUPPGSIZE;
      uvmunmap(pagetable, SUPPGROUNDUP(newsz), npages, 1);
    }
  } else {
    if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
      int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
      if (newsz == 0) {
        newsz = PGSIZE;
        npages--;
      }
      uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
    }
  }

  return newsz;
}

// Recursively free page-table pages.
// All leaf mappings must already have been removed.
void
freewalk(pagetable_t pagetable)
{
  // there are 2^9 = 512 PTEs in a page table.
  for(int i = 0; i < 512; i++){
    pte_t pte = pagetable[i];
    if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
      // this PTE points to a lower-level page table.
      uint64 child = PTE2PA(pte);
      freewalk((pagetable_t)child);
      pagetable[i] = 0;
    } else if(pte & PTE_V){
      panic("freewalk: leaf");
    }
  }
  kfree((void*)pagetable);
}

// Free user memory pages,
// then free page-table pages.
void
uvmfree(pagetable_t pagetable, uint64 sz)
{
  if(sz > 0)
    uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
  freewalk(pagetable);
}

// Given a parent process's page table, copy
// its memory into a child's page table.
// Copies both the page table and the
// physical memory.
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;
  char *mem;
  int pgsize = PGSIZE;
  for(i = PGSIZE; i < sz; i += pgsize){
    if((pte = walk(old, i, 0)) == 0)
      panic("uvmcopy: pte should exist");
    if((*pte & PTE_V) == 0)
      panic("uvmcopy: page not present");
    pa = PTE2PA(*pte);
    flags = PTE_FLAGS(*pte);
    int suppg = pa >= SUPSTART;
    pgsize = suppg ? SUPPGSIZE : PGSIZE;
    if((mem = suppg ? kalloc_suppage() : kalloc()) == 0)
      goto err;
    memmove(mem, (char*)pa, pgsize);
    if(mappages(new, i, pgsize, (uint64)mem, flags) != 0){
      suppg ? kfree_suppage(mem) : kfree(mem);
      goto err;
    }
  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}

// mark a PTE invalid for user access.
// used by exec for the user stack guard page.
void
uvmclear(pagetable_t pagetable, uint64 va)
{
  pte_t *pte;
  
  pte = walk(pagetable, va, 0);
  if(pte == 0)
    panic("uvmclear");
  *pte &= ~PTE_U;
}

// Copy from kernel to user.
// Copy len bytes from src to virtual address dstva in a given page table.
// Return 0 on success, -1 on error.
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;
  pte_t *pte;
  int suppg = dstva >= SUPSTART;
  int pgsize = suppg ? SUPPGSIZE : PGSIZE;
  while(len > 0){
    va0 = suppg ? SUPPGROUNDDOWN(dstva) : PGROUNDDOWN(dstva);
    if(va0 >= MAXVA)
      return -1;  
    pte = walk(pagetable, va0, 0);
    if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
       (*pte & PTE_W) == 0)
      return -1;
    pa0 = PTE2PA(*pte);
    n = pgsize - (dstva - va0);
    if(n > len)
      n = len;
    memmove((void *)(pa0 + (dstva - va0)), src, n);

    len -= n;
    src += n;
    dstva = va0 + pgsize;
  }
  return 0;
}

// Copy from user to kernel.
// Copy len bytes to dst from virtual address srcva in a given page table.
// Return 0 on success, -1 on error.
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
  uint64 n, va0, pa0;
  
  int suppg = srcva >= SUPSTART;
  int pgsize = suppg ? SUPPGSIZE : PGSIZE;
  while(len > 0){
    va0 = suppg ? SUPPGROUNDDOWN(srcva) : PGROUNDDOWN(srcva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = pgsize - (srcva - va0);
    if(n > len)
      n = len;
    memmove(dst, (void *)(pa0 + (srcva - va0)), n);

    len -= n;
    dst += n;
    srcva = va0 + pgsize;
  }
  return 0;
}

// Copy a null-terminated string from user to kernel.
// Copy bytes to dst from virtual address srcva in a given page table,
// until a '\0', or max.
// Return 0 on success, -1 on error.
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
  uint64 n, va0, pa0;
  int got_null = 0;
  int suppg = srcva >= SUPSTART;
  int pgsize = suppg ? SUPPGSIZE : PGSIZE;
  
  while(got_null == 0 && max > 0){
    va0 = suppg ? SUPPGROUNDDOWN(srcva) : PGROUNDDOWN(srcva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = pgsize - (srcva - va0);
    if(n > max)
      n = max;

    char *p = (char *) (pa0 + (srcva - va0));
    while(n > 0){
      if(*p == '\0'){
        *dst = '\0';
        got_null = 1;
        break;
      } else {
        *dst = *p;
      }
      --n;
      --max;
      p++;
      dst++;
    }

    srcva = va0 + pgsize;
  }
  if(got_null){
    return 0;
  } else {
    return -1;
  }
}

上述完成,我们其实已经可以在kvmmake(void)时,验证KERNEL在初始化PAGETABLE时,是否使用了巨页优化。

  // PLIC
  kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W);

我们简单做个KERNEL PAGETABLE的VMPRINT来验证:


1699201102087.png

可以看到这里2页,直接用到了巨页优化,因为并没有创建LEVEL 2的叶子节点。并且物理地址是根据2MB对齐。


1699201149163.png

4. umalloc.c

下面为了写一个用户命令来验证malloc可以使用巨页优化,我们需要对sbrk做一些改动。之前malloc最小申请的内存单位是PGSIZE,所以我们可以确保进程实际用的memory size 是连续的,那么sbrk就可以直接返回上次的addr,然后进程继续往后读是没有问题的。但是引入了巨页后,情况不一样了。
比如我们申请的内存为SUPPGSIZE + 4096,我们实际会分配2个巨页大小。也就是说发生了我们实际申请的size 和系统给的size有GAP,为了不浪费这段多申请的内存,我们需要有个途径返回实际得到的size。
针对这段测试代码:

#include "kernel/types.h"
#include "user/user.h"


int main(int argc, char *argv[]) {
    vmprint(1);
    // 让VA 和2MB对齐
    char *long_content = malloc(2076656);
    long_content[0] = 'c';
    printf("content:%c\n", long_content[0]);
    vmprint(1);
    // 分配一整个巨页,-16是因为有sizeof(Header) overhead.
    char *long_content2 = malloc(2097152-16);
    long_content2[0] = 'b';
    printf("content:%c\n", long_content2[0]);
    vmprint(1);
    char *long_content3 = malloc(2097152);
    long_content3[0] = 'a';
    printf("content:%c\n", long_content3[0]);
    vmprint(1);
    char *long_content4 = malloc(4096 - 16);
    long_content4[0] = 'd';
    printf("content:%c\n", long_content4[0]);
    vmprint(1);
    char *long_content5 = malloc(2097152 - 32 - 4096);
    long_content5[0] = 'e';
    printf("content:%c\n", long_content5[0]);
    vmprint(1);

    free(long_content5);
    free(long_content4);
    free(long_content3);
    free(long_content2);
    free(long_content);
    
    exit(0);
}

可以看到优化前, 用了2个大页,之后还要开512普通页。但是内存花的


1699234164537.png

优化后,继续利用之前大页没用完的内存,内存空间得到节约:


1699234250535.png
下面是优化代码:
1699234307649.png

exec.c, sysproc.c

这块loadseg 也需要enhance一下,巨页逻辑

static int
loadseg(pagetable_t pagetable, uint64 va, struct inode *ip, uint offset, uint sz)
{
  uint i, n;
  uint64 pa;
  int pgsize = PGSIZE;
  for(i = 0; i < sz; i += pgsize){
    pa = walkaddr(pagetable, va + i);
    if (pa >= SUPSTART) pgsize = SUPPGSIZE;
    if(pa == 0)
      panic("loadseg: address should exist");
    if(sz - i < pgsize)
      n = sz - i;
    else
      n = pgsize;
    if(readi(ip, 0, (uint64)pa, offset+i, n) != n)
      return -1;
  }
  
  return 0;
}

之前pgaccess 加的也要ENHANCE一下巨页逻辑

answers-pgtbl.txt

Q: Which other xv6 system call(s) could be made faster using this shared page? Explain how.

A:
uptime(): This system call returns the current uptime of the system. Although this value changes constantly, the kernel could update the shared page at fixed time intervals (e.g., every 10 milliseconds).
how: add timeticks value in usyscall structure, and when clockintr, it assign latest value to this page. and in userspace, when it call uuptime, it just fetch this value from USYSCALL Page like ugetpid().

Q: For every leaf page in the vmprint output, explain what it logically contains and what its permission bits are. Figure 3.4 in the xv6 book might be helpful, although note that the figure might have a slightly different set of pages than the init process that's being inspected here.

A:
for example,
.. .. .. 0: pte 0x00000000219da81b pa 0x000000008676a000
pte use last 10 bit to save flag, and 54-10 bit to save pa;
so pa = (pte >> 10) << 12;
the last 10 bit is 0000011011, which means V, R, X, U is enabled.

上一篇 下一篇

猜你喜欢

热点阅读