lab5 lazy

lab5 lazy_第1张图片

文章目录

  • Eliminate allocation from sbrk()
  • Lazy allocation
    • task
    • hints
    • 实现
  • Lazytests and Usertests
    • task
    • hints
    • 实现

Eliminate allocation from sbrk()

第一个任务是去阻止sysproc.c中的sys_sbrk()函数真的分配内存,只需要增p->sz即可

一行代码+注释即可

uint64
sys_sbrk(void) {
    int addr;
    int n;

    if (argint(0, &n) < 0)
        return -1;
    addr = myproc()->sz;
    myproc()->sz += n;
    // if(growproc(n) < 0)
    //   return -1;
    return addr;
}

这时候会发生两个错误

lab5 lazy_第2张图片

  1. usertrap的错误
    1. 页面错误是由硬件触发的,当硬件发现pte中的有效位为0时,就会陷入内核,并且设置r_scause为页错误相关的标志
  2. uvmunmap错误
    1. 有可能一个进程申请了一块内存,但是却没有真正使用,所以页表中是没有对应的一项的
    2. 结果在之后销毁这块内存的时候,还是会去页表中删除这一项,这一删就发现, 根本没有被mapped

Lazy allocation

task

  1. 修改trap.c中的代码,使其能够对用户空间的页错误进行反应

    通过映射一个新分配的物理内存页到出错的地址,并返回到用户空间使得进程继续执行

  2. 视情况修改其他的内核代码

hints

  1. 你可以通过检查r_scause是13还是15来判断是否是一个页错误
  2. r_stval()存储了引起错误的虚拟地址
  3. uvmalloc函数学习,你需要调用kallocmappages
  4. 使用PGROUNDDOWN(va)去得到虚拟页面的地址
  5. 修改uvmunmap()使其在碰到一个页面没有被mapped时不要panic
  6. 如果内核崩了,在汇编代码中查看sepc
  7. 使用你的vmprint函数去打印页表
  8. 如果出现incomplete type proc,去includespinlock.hproc.h

实现

usertrap中增加一个else if分支,关键操作就是kalloc和mappages,即申请一个操作,再增加一个映射

    } else if (r_scause() == 13 || r_scause() == 15) {
        uint64 va = r_stval();
        // 虚拟地址本来就不合法
        if (va >= p->sz) {
            p->killed = 1;
        } else {
            uint64 pa = (uint64)kalloc();
            // 内存用完了, 杀死进程
            if (pa == 0) {
                p->killed = 1;
            } else {
                va = PGROUNDDOWN(va);
                // 增加映射,如果失败了
                if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W | PTE_X | PTE_R | PTE_U) != 0) {
                    kfree((void *)pa);
                    p->killed = 1;
                }
            }
        }
    } 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;
    }

uvmunmap中修改为

        if ((*pte & PTE_V) == 0)
            continue;

就成功了

image-20230822163902501

Lazytests and Usertests

task

  1. 通过lazytestsusertests

hints

  1. 处理sbrk的参数为负数的情况
  2. 如果一个进程访问的虚拟地址超过了通过sbrk分配的
  3. 正确处理fork中父进程到子进程的内存拷贝
  4. 正确处理一个进程通过系统调用read或者write传递了一个合法的虚拟地址,但是对应的内存地址却没有被分配
  5. 正确处理内存用完的限制,即kalloc如果失败,则杀死进程
  6. 处理用户堆栈下面无用的页面错误,即guardpage

一个一个hins来

实现

第1个是参数为负数,特判一下就行,如果是负数,就按原来的方式处理

    if (n > 0) {
        myproc()->sz += n;
    } else {
        if (growproc(n) < 0)
            return -1;
    }

第2个和第6个通过这个判断就可以搞定

va >= p->sz || va < p->trapframe->sp

有个之前没搞清楚的点,那就是通过p->trapframe->sp取得的是栈顶指针,栈指针又是向下走的。而栈指针往下,除了一个guardpage是没有映射之外,其他的page都应该是有映射的,即pte的有效位为0。因此,如果引发了页故障并且地址还低于栈顶指针,说明肯定是碰到了guardpage的

第3个其实说的是uvmcopy函数,这个函数就是将父进程的页表原模原样的拷贝给子进程,但是有的虚拟页面连父节点自己都没有映射,子节点哪来的映射,因此碰到找不到pte或者pte有问题的情况,直接continue就行了

        if ((pte = walk(old, i, 0)) == 0)
            continue;
        // panic("uvmcopy: pte should exist");
        if ((*pte & PTE_V) == 0)
            continue;
        // panic("uvmcopy: page not present");

第4个的问题在于,如果是系统调用,那它会先在usertrap中进入syscall的处理流程,不会进入我们新添加的流程,所以会有问题

通过对write函数的不断追踪,可以发现,牵扯到的是walkaddr函数。其实就是现在需要使用这个地址了,但是原始的walkaddr不能正确返回一个物理地址,因为压根就没有。所以在walkaddr中增加创建映射的代码即可

    if ((*pte & PTE_V) == 0) {
        struct proc *p = myproc();
        // 地址不合法
        if (va >= p->sz || va < p->trapframe->sp) {
            return 0;
        }
        uint64 pa = (uint64)kalloc();
        if (pa == 0) {
            panic("memory run out\n");
            return 0;
        }
        if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W | PTE_X | PTE_R | PTE_U) != 0) {
            kfree((void *)pa);
            return 0;
        }
        // *pte = PA2PTE(pa) | PTE_W | PTE_X | PTE_R | PTE_U | PTE_V;
    }

第5点,只要你用了kalloc后判断了它的返回值,然后panic就行

你可能感兴趣的:(6.S081,java,服务器,网络)