美文网首页
lab5: lazy copy

lab5: lazy copy

作者: sarto | 来源:发表于2022-06-22 11:04 被阅读0次

通常,用户程序使用 sbrk 进行内存扩容,这里存在两个问题

  1. 用户频繁调用 sbrk,如果每次都进行内存分配,耗费时间
  2. 用户可能分配了许多,但是并不一定用这么多,导致浪费

基于这两点,懒分配就变得有必要了,当一个进程进行内存扩容时,内核仅记录其分配的大小,并不实际进行分配,只有当使用的时候,进行内存分配。

懒分配的流程

  1. 进程请求进行 n 个字节的内存空间扩容
  2. 内核修改该进程的空间大小标识 p->sz += n,不进行实际分配
  3. 进程访问分配的地址
  4. 页表项不存在,触发中断
  5. 中断捕获,通过 s_tval 得知异常地址,对该地址所处的页进行内存分配
  6. 从中断返回,进程重新执行该指令,正常执行

xv6 懒分配实现

  1. growproc 在扩容这里,取消扩容的实际实现,但是要加上限定条件,如果扩容大小超过最大虚拟地址,则返回错误,同时,为了保证应用程序的合理性,缩容不采用懒分配策略。
int 
growproc(int n)
{
  uint sz;
  struct proc *p = myproc();

  sz = p->sz;
  if(n > 0){
    if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
      return -1;
    }
  } else if(n < 0){
    sz = uvmdealloc(p->pagetable, sz, sz + n);
  }
  p->sz = sz;
  return 0;
}

  1 int
239 growproc(int n)                                                                                                                                                                           
  1 {
  2   uint sz;
  3   struct proc *p = myproc();
  4 
  5   sz = p->sz;
  6   if (n > 0) {
  7       sz = sz + n;
  8       if (sz > MAXVA)
  9           return -1;
 10   } else if (n < 0) {
 11     sz = uvmdealloc(p->pagetable, sz, sz + n);
 12   }
 13   p->sz = sz;
 14   return 0;
 15 }

  1. usertrap.c 在原来的基础上加上 r_scause == 13 || r_scause == 15的处理逻辑。也就是读写内存地址异常。
 36 void
 35 usertrap(void)
 34 {
 33   int which_dev = 0;
 32 
 31   if((r_sstatus() & SSTATUS_SPP) != 0)
 30     panic("usertrap: not from user mode");
 29 
 28   // send interrupts and exceptions to kerneltrap(),
 27   // since we're now in the kernel.
 26   w_stvec((uint64)kernelvec);
 25 
 24   struct proc *p = myproc();
 23   
 22   // save user program counter.
 21   p->trapframe->epc = r_sepc();
 20   
 19   if(r_scause() == 8){
 18     // system call
 17 
 16     if(p->killed)
 15       exit(-1);
 14 
 13     // sepc points to the ecall instruction,
 12     // but we want to return to the next instruction.
 11     p->trapframe->epc += 4;
 10 
  9     // an interrupt will change sstatus &c registers,
  8     // so don't enable until done with those registers.
  7     intr_on();
  6 
  5     syscall();
  4   } else if((which_dev = devintr()) != 0){
  3     // ok
  2   } else if(r_scause() == 13 || r_scause() == 15) {
  1       uint64 va = PGROUNDDOWN(r_stval());
72        if (va >= p->sz) {                                                                                                                                                                  
  1           //printf("invalid address: %d\n", va);
  2           p->killed = 1;
  3           goto err;
  4       }
  5       uint64 pa = (uint64)kalloc();
  6       mappages(p->pagetable, va, PGSIZE, pa, PTE_V|PTE_R|PTE_W|PTE_U);
  7   } else {
  8     printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
  9     printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
 10     p->killed = 1;
 11   }
  1. vm.c 关于 p->sz 含义更改的一些处理
    以前 p->sz 代表进程实际占用的地址空间大小,而现在意味着这些地址可能未实际分配,含义的更改导致一些使用该变量的函数需要作出相应的改变。
    uvmunmap 将在释放进程时调用,其释放虚拟地址的范围是 p->sz,但是 p->sz 存在一些未映射的部分,所以当这些地址未映射时,不进行释放,直接跳过即可。
 14 void
 13 uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
 12 {
 11   uint64 a;
 10   pte_t *pte;
  9 
  8   if((va % PGSIZE) != 0)
  7     panic("uvmunmap: not aligned");
  6 
  5   for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
  4     if((pte = walk(pagetable, a, 0)) == 0)
  3         continue;
  2     if((*pte & PTE_V) == 0)
  1         continue;
187     if(PTE_FLAGS(*pte) == PTE_V)                                                                                                                                                          
  1       panic("uvmunmap: not a leaf");
  2     if(do_free){
  3       uint64 pa = PTE2PA(*pte);
  4       kfree((void*)pa);
  5     }
  6     *pte = 0;
  7   }
  8 }

uvmcopy 在进程 fork 时使用,拷贝父进程的地址空间,而父进程的虚拟地址空间为 p->sz。所以当这些地址空间有一些未实际映射时,直接跳过。

 34 int
 33 uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
 32 {
 31   pte_t *pte;
 30   uint64 pa, i;
 29   uint flags;
 28   char *mem;
 27 
 26   for(i = 0; i < sz; i += PGSIZE){
 25     if((pte = walk(old, i, 0)) == 0)
 24         continue;
 23     if((*pte & PTE_V) == 0)
 22         continue;
 21     pa = PTE2PA(*pte);
 20     flags = PTE_FLAGS(*pte);
 19     if((mem = kalloc()) == 0)
 18       goto err;
 17     memmove(mem, (char*)pa, PGSIZE);
 16     if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
 15       kfree(mem);
 14       goto err;
 13     }
 12   }
 11   return 0;
 10 
  9  err:
  8   uvmunmap(new, 0, i / PGSIZE, 1);
  7   return -1;
  6 }

具体代码记录
https://github.com/merore/xv6-labs-2020/commit/a247d13e5287ae71abb708decb6af872b218a5da

相关文章

  • lab5: lazy copy

    通常,用户程序使用 sbrk 进行内存扩容,这里存在两个问题 用户频繁调用 sbrk,如果每次都进行内存分配,耗费...

  • IOS常用代码段

    copy strong weak assign lazy mark

  • 【r<-高级】内部机制

    内容: 惰性求值 (Lazy evaluation)复制-修改机制 (Copy-on-modify mechani...

  • 硬件上机第五次实验

    LAB5 3.56 题目描述 consider the following assembly code x a...

  • Lab5

    Lab5 report 练习0:填写已有实验 用meld对比修改了以下文件: 其中需要对trap.c和proc.c...

  • lab5

    先checksec 一波 开启了NX保护,静态链接,溢出的空间也够构造rop链,一看就是return to sys...

  • 函数的语法

    函数 -> lazy_sum 函数调用 -> lazy_sum() 函数调用的结果 -> f = lazy_sum...

  • Kotlin的by lazy与lateinit区别

    1、by lazy 1.1、by lazy 惰性加载->用到的时候在加载的意思1.2 by lazy 知道具体值...

  • Swift ~ Lazy Stored Properties

    官方文档 ~ Lazy Stored Properties Lazy Stored PropertiesA laz...

  • MachO动态库绑定过程详解

    动态库绑定过程详解 首先动态库绑定分为lazy bind 和no_lazy_bind,lazy bind 主要用于...

网友评论

      本文标题:lab5: lazy copy

      本文链接:https://www.haomeiwen.com/subject/ojxxqrtx.html