xv6的mmap的实现 | Blurred code

xv6的mmap的实现

2021/07/18

Updated:2021/09/22

Categories: xv6

很长一段时间没整理xv6的相关笔记了。mmap是Linux下的一个比较常用的api,可以将磁盘的文件映射到虚拟内存中。 实现mmap需要用到lazy分配的机制,这样才允许mmap映射远大于内存的文件到虚拟内存中。

Lazy 分配的实现

常规的添加两个syscall用于mmapmunmap。mit的实验中不需要实现mmap的第一个参数addr,交由内核来决定,最后一个参数offset不需要实现,flag只需要实现map_privatemap_shared

proc中新建struct vma作为slots,由于mmap的测试量比较小,官方给了提示可以直接开栈上数组,不用走kalloc分配堆内存。在mmapsyscall里处理逻辑,

  struct vma* v = 0;
  for(int i = 0; i < 16;i++)
  {
      if(!p->vmas[i].valid_) // we find one
      {
          v = &(p->vmas[i]);
          v->valid_ = 1;
          addr = p->sz;
          v->addr_ = addr;
          v->length_ = length;
          v->flags_ = flags;
          v->prot_ = prot;
          v->file_ptr_ = f;
          filedup(f); //重要:任何一个mmap都要维护文件的引用计数
          break;
      }
  }

同时负责参照lazy分配的实现,在系统中断处处理Page Fault的中断,注意边界情况的处理。

一个合法的page fault的触发要验证:

      uint64 ka = (uint64)kalloc();
      if(ka == 0) goto bad;
      memset((void*)ka,0,PGSIZE);
      va = PGROUNDDOWN(va);
      int permission = PTE_U;
      if(v->prot_ & PROT_READ)
        permission |= PTE_R;
      if(v->prot_ & PROT_WRITE)
        permission |= PTE_W;
      struct vma* v = &p->vmas[i];
      ilock(v->file_ptr_->ip);
      readi(v->file_ptr_->ip,0,ka,va - v->addr_,PGSIZE);
      iunlock(v->file_ptr_->ip);
      if(mappages(p->pagetable,va,PGSIZE,ka,permission) < 0)
      {
          kfree((void*)ka);
          goto bad;
      }
      goto good;
bad:
    p->killed = 1;
good:
    (void)0;
  }

munmap的实现

没什么好说的,注意处理四种不同的情况

fork的处理

子进程要继承父进程的所有的mmap区域,但是可以不处理页表,这样子进程的mmap读取的时候会重新触发page fault。在Linux里这里应该要细化的处理,尽量使子进程的页表和父进程的页表形成cow,会节约物理内存的使用。

主要要维护文件描述符fd的引用计数,在exit函数的时候减少所有被mmap区域的引用计数,并且如果有map_shared的区域要把更改写回到硬盘文件。

  memcpy(np->vmas,p->vmas,sizeof(struct vma) * 16);
  for(int i = 0;i<16;i++)
  {
      if(np->vmas[i].valid_)
      {
          filedup(np->vmas[i].file_ptr_);
      }
  }