Skip to content
Go back

MIT6.S081 Lab mmap

Edit page

参考博客:Xiao Fan(樊潇)

实验目的:

首先是将 $U/_mmaptest\ 添加到 Makefile,然后添加 mmap()munmap() 系统调用,这里不再赘述。

在 proc.h 中添加对 struct vma 的定义:

struct vma {
  int valid;
  uint64 addr;
  int length;
  int prot;
  int flags;
  struct file *mapfile;
};

// Per-process state
struct proc {
  // ...
  struct vma vmas[NVMA];       // Process vmas
};

在 param.h 中添加 NVMA

#define NVMA 16 // number of process vmas

在 sysfile.c 中添加 sys_mmap()

uint64 sys_mmap(void) {
  int length, prot, flags, fd;
  struct proc *p = myproc();
  struct file *mapfile;

  // get argument
  if (argint(1, &length) < 0 || argint(2, &prot) < 0 ||
      argint(3, &flags) < 0 || argfd(4, &fd, &mapfile) < 0)
    return -1;

  // check
  length = PGROUNDDOWN(length);
  if(MAXVA - length < p->sz)
    return -1;
  if (!mapfile->readable && (prot & PROT_READ))
    return -1;
  if (!mapfile->writable && (prot & PROT_WRITE) && (flags & MAP_SHARED))
    return -1;

  // find a free vma and contain it
  for (int i = 0; i < NVMA; ++i) {
    struct vma *curvma = &p->vmas[i];
    if (!curvma->valid) {
      curvma->valid = 1;
      curvma->addr = p->sz;
      p->sz += length;
      curvma->length = length;
      curvma->flags = flags;
      curvma->prot = prot;
      curvma->mapfile = mapfile;
      filedup(mapfile);
      return curvma->addr;
    }
  }

  // no free vma
  return -1;
}

接下来在 usertrap() 中添加对页错误的处理,实现延迟加载:

} else if (r_scause() == 13 || r_scause() == 15) { // Page Fault
    uint64 va = r_stval();
    struct proc *p = myproc();
    struct vma *vmas = p->vmas;

    // check va safe
    if (va > MAXVA || va >= p->sz)
      goto exception;

    // lazy allocation
    for (int i = 0; i < NVMA; ++i) {
      struct vma *curvma = &vmas[i];
      if (curvma->valid && va >= curvma->addr &&
          va < curvma->addr + curvma->length) {
        va = PGROUNDDOWN(va);
        uint64 pa = (uint64)kalloc();
        if (pa == 0)
          goto exception;
        memset((void *)pa, 0, PGSIZE);
        ilock(curvma->mapfile->ip);
        if (readi(curvma->mapfile->ip, 0, pa, va - curvma->addr, PGSIZE) < 0) {
          iunlock(curvma->mapfile->ip);
          break;
        }
        iunlock(curvma->mapfile->ip);
        int flag = (curvma->prot << 1) | PTE_V | PTE_U;
        if (mappages(p->pagetable, va, PGSIZE, pa, flag) < 0) {
          kfree((void*)pa);
          break;
        }
        break;
      }
    } 
  } else {
      exception:
      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;
    }

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

    // give up the CPU if this is a timer interrupt.
    if (which_dev == 2)
      yield();

    usertrapret();
  }

接下来实现 munmap()

uint64 sys_munmap(void) {
  uint64 addr;
  int length;
  struct proc *p = myproc();
  
  // get argument
  if (argaddr(0, &addr) < 0 || argint(1, &length) < 0)
    return -1;

  // look for vma
  struct vma *vma = 0;
  int found = 0;
  for (int i = 0; i < NVMA; ++i) {
    vma = &p->vmas[i];
    if (vma->valid && addr >= vma->addr && addr < vma->addr + vma->length) {
      found = 1;
      break;
    }
  }
  
  // not found
  if (!found)
    return -1;

  addr = PGROUNDDOWN(addr);
  length = PGROUNDDOWN(length);
  if (vma->flags & MAP_SHARED) {
    // if MAP_SHARED then write back first
    if (filewrite(vma->mapfile, addr, length) < 0)
      printf("munmap: filewrite < 0\n");
  }

  // unmapped
  uvmunmap(p->pagetable, addr, length / PGSIZE, 1);

  if (addr == vma->addr) {
    if (length == vma->length) {
      // unmapped whole vma
      fileclose(vma->mapfile);
      vma->valid = 0;
      p->sz -= length;
    } else {
      // unmapped from start to middle
      vma->addr += length;
      vma->length -= length;
    } 
  } else if (addr + length == vma->addr + vma->length) {
    // unmapped from middle to end
    vma->length -= length;
  } else {
    return -1;
  }

  return 0;
}

更新 fork()exit()

// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int fork(void)
{
  int i, pid;
  struct proc *np;
  struct proc *p = myproc();

  // Allocate process.
  if((np = allocproc()) == 0){
    return -1;
  }

	// Copy vma from parent to child
  for (int i = 0; i < NVMA; i++) {
    if (p->vmas[i].valid) {
      memmove(&np->vmas[i], &p->vmas[i], sizeof(struct vma));
      filedup(np->vmas[i].mapfile);
    }
  }

  // ...
}

// Exit the current process.  Does not return.
// An exited process remains in the zombie state
// until its parent calls wait().
void exit(int status)
{
  struct proc *p = myproc();

  if(p == initproc)
    panic("init exiting");

  // Close all open files.
  for(int fd = 0; fd < NOFILE; fd++){
    if(p->ofile[fd]){
      struct file *f = p->ofile[fd];
      fileclose(f);
      p->ofile[fd] = 0;
    }
  }

  // unmap all vma
  for (int i = 0; i < NVMA; ++i) {
    if (p->vmas[i].valid) {
      if (p->vmas[i].flags & MAP_SHARED) {
        filewrite(p->vmas[i].mapfile, p->vmas[i].addr, p->vmas[i].length);
      }
      fileclose(p->vmas[i].mapfile);
      uvmunmap(p->pagetable, p->vmas[i].addr, p->vmas[i].length / PGSIZE, 1);
      p->vmas[i].valid = 0;
    }
  }

  // ...
}

到这一步依然存在许多 bug,导致无法通过测试。

munmap() 中存在 unmap from start to middleunmap from middle to end 的情况,这就导致在 p->sz 以内的内存并不一定都有映射,因此可能会造成 uvmunmap()uvmcopy()panic,需要作以下修改:

// uvmcopy
if((*pte & PTE_V) == 0)
     // panic("uvmcopy: page not present");
     continue;
  
// uvmunmap  
// if((*pte & PTE_V) == 0)
//   panic("uvmunmap: not mapped");

另外 kfree 会试图解放 0 这个物理内存,参考博客的作者没有给出原因,我在上一个 bug 耗费了太多时间和精力,因此也没有弄明白,直接作修改吧:

void
kfree(void *pa)
{
  struct run *r;
  if (pa == 0)
    return;

总结:


Edit page
Share this post on:

Previous Post
2025-08-31
Next Post
MIT6.S081 Lab fs