MIT 6.S08操作系统实验课 lab5 懒分配

提示:这是操作系统本人对 MIT 6.S081 的 lab5 实验课的笔记,仅供参考。


前言

参考资料:
资料1
资料2


一、实验目标

实现一个内存页懒分配机制,在调用 sbrk() 的时候,不立即分配内存,而是只作记录。在访问到这一部分内存的时候才进行实际的物理内存分配。

二、实验

本次LAB的三个小节都是为整体实现内存懒分配而设计,因此本LAB将三小节整体综合在一起进行叙述。
第一步
修改文件kernel/sysproc.c中的sys_sbrk函数,修改后函数内容如下所示:

uint64
sys_sbrk(void)
{
  int addr;
  int n;
  struct proc *p = myproc();
  if(argint(0, &n) < 0)
    return -1;
  // if(growproc(n) < 0)
  //   return -1;
  if(n < 0){ // n为负数,立即释放内存
    uvmdealloc(p->pagetable, p->sz, p->sz+n);
  }
  p->sz += n;  // 进程空间大小增加n,但是并没有实际进行物理内存分配
  addr = p->sz;
  return addr;
}

第二步
向文件kernel/vm.c添加如下两个函数,并在文件kernel/defs.h文件中添加函数声明:

// 在defs.h文件中添加的函数声明
int             uvmshouldtouch(uint64 va);
void            uvmlazytouch(uint64 va);

/**** 在vm.c ****/添加的两个函数

// 为懒分配的页建立从虚拟地址到物理地址的映射
void uvmlazytouch(uint64 va) {
  struct proc *p = myproc();
  char *mem = kalloc();  // 分配一个物理页
  if(mem == 0) {  // 检测是否分配成功
    printf("lazy alloc: out of memory\n");
    p->killed = 1;
  } else {
    memset(mem, 0, PGSIZE);  // 初始化为0
    if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){  // 建立映射
      printf("lazy alloc: failed to map page\n");
      kfree(mem);
      p->killed = 1;
    }
  }
}

// 检测在懒分配机制下,一个虚拟地址是否应该被映射到物理页
int uvmshouldtouch(uint64 va) {
  pte_t *pte;
  struct proc *p = myproc();
  uint64 sp = r_sp();

  return va < p->sz // 首先该虚拟地址必须在[0, p->sz)范围内
    &&  (PGROUNDDOWN(sp) <= va || va < PGROUNDDOWN(sp) - 4096) // 这里是防止分配的虚拟地址为栈保护页,即guard page,可以看下进程的虚拟空间分配图
    && (((pte = walk(p->pagetable, va, 0))==0) || ((*pte & PTE_V)==0)); // 该虚拟必须没有映射到物理页
}

第三步
修改kernel/trap.c文件中的usertrap函数,添加检测缺页异常的代码,并为发生异常的地址是由于懒分配而没有映射的话,就为其分配物理内存,在页表建立映射:

void
usertrap(void)
{
 //  .......
  if(r_scause() == 8){
    // system call

    if(p->killed)
      exit(-1);

    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->trapframe->epc += 4;

    // an interrupt will change sstatus &c registers,
    // so don't enable until done with those registers.
    intr_on();

    syscall();
  } else if((which_dev = devintr()) != 0){
    // ok
  } else {  // 这里开始修改................................................
    uint64 va = r_stval(); // 获取发生异常的虚拟地址
    if((r_scause() == 13 || r_scause() == 15) && uvmshouldtouch(va)){ // 缺页异常,并且发生异常的地址进行过懒分配
      uvmlazytouch(va); // 分配物理内存,并在页表创建映射
    }else{
      printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
      printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
      p->killed = 1;
    }
  }

 // ..........
 
  usertrapret();
}

第四步
由于懒分配的页,在刚分配的时候是没有对应的映射的,所以要把一些原本在遇到无映射地址时会 panic 的函数的行为改为直接忽略这样的地址。
主要是kernel/vm.c文件中的uvmummap函数和uvmcopy函数

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");

  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if((pte = walk(pagetable, a, 0)) == 0)
      continue;         // 如果页表项不存在,跳过当前地址 (原本是直接panic)
    if((*pte & PTE_V) == 0)
      continue;         // 这是是因为walk没有检查最后返回的表项是否有效,因为前walk只检查了前两及页表项
    if(PTE_FLAGS(*pte) == PTE_V)
      panic("uvmunmap: not a leaf");
    if(do_free){
      uint64 pa = PTE2PA(*pte);
      kfree((void*)pa);
    }
    *pte = 0;
  }
}

int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;
  char *mem;

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      continue; // 如果一个页不存在,则认为是懒加载的页,忽略即可
    if((*pte & PTE_V) == 0)
      continue; // 如果一个页不存在,则认为是懒加载的页,忽略即可
    pa = PTE2PA(*pte);
    flags = PTE_FLAGS(*pte);
    if((mem = kalloc()) == 0)
      goto err;
    memmove(mem, (char*)pa, PGSIZE);
    if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
      kfree(mem);
      goto err;
    }
  }
  return 0;

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

第五步
主要是kernel/vm.c文件中的copyin函数和copyout函数可能会访问到懒分配但是还没实际分配的页,所以要加一个检测,确保 copy 之前,用户态地址对应的页都有被实际分配和映射。

int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;

  if(uvmshouldtouch(dstva))  // 检测是否建立映射
    uvmlazytouch(dstva);

  // ......

}

int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
  uint64 n, va0, pa0;

  if(uvmshouldtouch(srcva))  // 检测是否建立了映射
    uvmlazytouch(srcva);

  // ......

}

至此,所有操作步骤完成,输入make命令进行编译,make qemu启动系统,输入lazytestsusertests命令进行测试。


  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值