全网最最实用--基于Mac ARM 芯片实现操作系统MIT 6.S081-lab10

实验十 文件内存映射

一、代码理解

1.物理内存布局

  1. 启动 ROM

    • 0x00001000:启动 ROM,由 QEMU 提供。
  2. CLINT(Core Local Interruptor)

    • 0x02000000:CLINT 寄存器基地址。
    • CLINT_MTIMECMP(hartid):每个 hart(硬件线程)的时间比较寄存器地址。
    • CLINT_MTIME:自启动以来的周期数寄存器地址。
  3. PLIC(Platform-Level Interrupt Controller)

    • 0x0C000000:PLIC 寄存器基地址。
    • PLIC_PRIORITYPLIC_PENDINGPLIC_MENABLE(hart)PLIC_SENABLE(hart)PLIC_MPRIORITY(hart)PLIC_SPRIORITY(hart)PLIC_MCLAIM(hart)PLIC_SCLAIM(hart):PLIC 相关寄存器的偏移地址。
  4. UART0

    • 0x10000000:UART0 寄存器基地址。
    • UART0_IRQ:UART0 的中断号。
  5. VIRTIO0

    • 0x10001000:VirtIO 磁盘设备的寄存器基地址。
    • VIRTIO0_IRQ:VirtIO 磁盘设备的中断号。
  6. 内核使用的物理内存

    • 0x80000000:内核代码和数据的起始地址。
    • PHYSTOP:内核使用的物理内存的结束地址(0x80000000 + 128MB)。

2.内核内存布局

  1. 内核基地址

    • KERNBASE:内核代码和数据的起始物理地址(0x80000000)。
  2. 物理内存结束地址

    • PHYSTOP:内核使用的物理内存的结束地址(0x80000000 + 128MB)。
  3. 跳板页(Trampoline Page)

    • TRAMPOLINE:跳板页的地址,位于用户和内核空间的最高地址(MAXVA - PGSIZE)。
  4. 内核栈

    • KSTACK(p):每个 hart 的内核栈地址,位于跳板页下方,每个栈被无效的守护页包围。
  5. 陷阱帧(Trapframe)

    • TRAPFRAME:陷阱帧的地址,位于跳板页下方(TRAMPOLINE - PGSIZE)。

3.用户内存布局

  1. 用户内存起始地址
    • 从地址 0 开始,依次是:
      • 代码段(text)
      • 原始数据和 bss 段
      • 固定大小的栈
      • 可扩展的堆
      • 陷阱帧(TRAPFRAME
      • 跳板页(TRAMPOLINE

二、mmap

1.题目描述

mmap和munmap系统调用允许 UNIX 程序对其地址空间施加详细控制。它们可用于在进程之间共享内存、将文件映射到进程地址空间,以及作为用户级页面错误方案(如讲座中讨论的垃圾收集算法)的一部分。在本实验中,您将向 xv6 添加mmap和munmap , 重点关注内存映射文件。

2.解答

要实现 mmapmunmap 系统调用,并在 xv6 内核中支持内存映射文件.

a. 添加 mmapmunmap 系统调用

首先,在 kernel/syscall.h 中定义系统调用号:

#define SYS_mmap 22
#define SYS_munmap 23

kernel/syscall.c 中添加系统调用入口:

extern uint64 sys_mmap(void);
extern uint64 sys_munmap(void);

static uint64 (*syscalls[])(void) = {
  [SYS_mmap]    sys_mmap,
  [SYS_munmap]  sys_munmap,
};

kernel/sysfile.c 中实现 sys_mmapsys_munmap

uint64 sys_mmap(void) {
  // For now, just return errors.
  return -1;
}

uint64 sys_munmap(void) {
  // For now, just return errors.
  return -1;
}
b. 定义 VMA 结构

kernel/proc.h 中定义 VMA 结构:

#define NVMA 16

struct vma {
  uint64 addr;      // Start address
  uint64 length;    // Length of the mapping
  int prot;         // Protection flags (PROT_READ, PROT_WRITE, etc.)
  int flags;        // Mapping flags (MAP_SHARED, MAP_PRIVATE, etc.)
  struct file *file; // File being mapped
  uint64 offset;    // Offset in the file
};

struct proc {
  struct vma vmas[NVMA];
  int nvma;
};
c. 实现 mmap

kernel/sysfile.c 中实现 sys_mmap

uint64 sys_mmap(void) {
  uint64 addr;
  uint64 length;
  int prot;
  int flags;
  int fd;
  uint64 offset;

  if (argaddr(0, &addr) < 0 || argint(1, &length) < 0 || argint(2, &prot) < 0 ||
      argint(3, &flags) < 0 || argfd(4, &fd, 0) < 0 || argaddr(5, &offset) < 0) {
    return -1;
  }

  struct file *f = filedup(myproc()->ofile[fd]);
  if (f == 0) {
    return -1;
  }

  struct proc *p = myproc();
  if (p->nvma >= NVMA) {
    fileclose(f);
    return -1;
  }

  // Find an unused region in the process's address space.
  uint64 map_addr = p->sz;
  p->sz += length;

  // Add a VMA to the process's table of mapped regions.
  p->vmas[p->nvma].addr = map_addr;
  p->vmas[p->nvma].length = length;
  p->vmas[p->nvma].prot = prot;
  p->vmas[p->nvma].flags = flags;
  p->vmas[p->nvma].file = f;
  p->vmas[p->nvma].offset = offset;
  p->nvma++;

  return map_addr;
}
d. 实现页面错误处理

kernel/trap.c 中处理页面错误:

void usertrap(void) {
  // ...
  if(r_scause() == 13 || r_scause() == 15) {
    uint64 va = r_stval();
    struct proc *p = myproc();

    for (int i = 0; i < p->nvma; i++) {
      if (va >= p->vmas[i].addr && va < p->vmas[i].addr + p->vmas[i].length) {
        uint64 pa = kalloc();
        if (pa == 0) {
          p->killed = 1;
          break;
        }

        ilock(p->vmas[i].file->ip);
        readi(p->vmas[i].file->ip, 0, pa, p->vmas[i].offset + (va - p->vmas[i].addr), PGSIZE);
        iunlock(p->vmas[i].file->ip);

        if (mappages(p->pagetable, va, PGSIZE, pa, p->vmas[i].prot) != 0) {
          kfree(pa);
          p->killed = 1;
        }
        break;
      }
    }
  }
  // ...
}
e. 实现 munmap

kernel/sysfile.c 中实现 sys_munmap

uint64 sys_munmap(void) {
  uint64 addr;
  uint64 length;

  if (argaddr(0, &addr) < 0 || argint(1, &length) < 0) {
    return -1;
  }

  struct proc *p = myproc();
  for (int i = 0; i < p->nvma; i++) {
    if (addr == p->vmas[i].addr && length == p->vmas[i].length) {
      uvmunmap(p->pagetable, addr, length / PGSIZE, 1);
      fileclose(p->vmas[i].file);
      p->nvma--;
      memmove(&p->vmas[i], &p->vmas[i + 1], (p->nvma - i) * sizeof(struct vma));
      return 0;
    }
  }

  return -1;
}
f. 修改 exitfork

kernel/proc.c 中修改 exitfork

void exit(int status) {
  struct proc *p = myproc();

  // Unmap all mapped regions.
  for (int i = 0; i < p->nvma; i++) {
    uvmunmap(p->pagetable, p->vmas[i].addr, p->vmas[i].length / PGSIZE, 1);
    fileclose(p->vmas[i].file);
  }
  p->nvma = 0;

  // ...
}

int fork(void) {
  int i, pid;
  struct proc *np;
  struct proc *p = myproc();

  // ...

  // Copy VMA information.
  np->nvma = p->nvma;
  memmove(np->vmas, p->vmas, sizeof(struct vma) * p->nvma);
  for (i = 0; i < p->nvma; i++) {
    filedup(p->vmas[i].file);
  }

  // ...
}

三、心得体会

a.研〇趣事

老师:我发现你性格有点急
我:啥?(他发现我最近在实习?!!)
老师:我发现你性格有点急,每次你说完就撂。
同门:我每次等老师说完拜拜,他退出会议我再退
我: 还好没发现我实习,呜呜,之后再也不一说完再见就结束会议了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值