跳至主要內容
Plus's NoteBook

Plus's NoteBook

项目名称
项目详细描述
链接名称
链接详细描述
书籍名称
书籍详细描述
文章名称
文章详细描述
伙伴名称
伙伴详细介绍
自定义项目
自定义项目
自定义详细介绍
Lab 4:traps

本实验探索系统调用如何实现陷入。你将使用栈先进行一些热身训练,之后你将实现一个用户计陷入处理的例子。

注意

在你开始编码之前,请阅读 xv6 书的第四章以及相关文件:

  • kernel/trampoline.S: 涉及从用户空间切换到内核空间以及返回的汇编过程。
  • kernel/trap.c: 处理所有中断的代码。

Plus大约 12 分钟操作系统MITXV6riscvC
Chapter 1 操作系统接口(Operating system interfaces)

操作系统的工作是在多个程序之间共享计算机,并提供比硬件本身支持的更有用的服务。操作系统管理和抽象低级硬件,使得例如文字处理器不必关心使用的是哪种类型的磁盘硬件。操作系统在多个程序之间共享硬件资源,使它们能够同时运行(或表现为同时运行)。最后,操作系统提供了受控的程序交互方式,以便它们可以共享数据或共同工作。

操作系统通过接口向用户程序提供服务。设计一个良好的接口实际上是困难的。一方面,我们希望接口简单且窄,因为这样更容易正确实现。另一方面,我们可能会想要向应用程序提供许多复杂的功能。解决这种张力的技巧在于设计依赖于少量机制的接口,这些机制可以组合提供很大的通用性。


Plus大约 24 分钟操作系统MITXV6riscv
Chapter 4 陷阱和系统调用 (Traps and system calls)

有三种事件会导致CPU暂停普通指令的执行,并且强制将控制权转换到处理该事件的特殊代码上。第一种情况是系统调用,当用户程序执行ecall指令时请求内核为其执行某些操作时。第二种情况是是一种异常,指令(用户或者内核)执行了非法操作,例如除以零或者使用了无效的虚拟地址。第三种情况是设备中断,当设备发出需要关注的信号时,例如当硬盘完成了读写请求。

这本书使用trap陷阱作为这些情况的通用术语。通常,任何在陷阱发生时正在执行的代码在之后都要被恢复,并且不该意识到发生了任何特殊事件。换句话说,我们通常希望陷阱是透明的;这对于中断尤其重要,因为被中断的代码通常不会预料到中断的发生。通常的顺序是一个陷阱强制把控制权转入内核;内核保存寄存器和其他状态这样正在执行的进程才能够被恢复;内核执行相应的处理程序代码(例如,系统调用实现或设备驱动程序);内核恢复保存的状态并从陷阱返回;原始代码在中断发生的地方恢复执行。


Plus大约 23 分钟操作系统MITXV6riscv
Chapter 2 操作系统的组织(Perating system organization)

一个操作系统的关键需求是支持同时进行多个活动。例如,使用第1章中描述的系统调用接口,一个进程可以使用fork启动新的进程。操作系统必须在这些进程之间分时共享计算机的资源。例如,即使进程的数量多于硬件CPU的数量,操作系统也必须确保所有进程都有机会执行。操作系统还必须安排进程之间的隔离。也就是说,如果一个进程有一个错误并且故障,不应该影响到那些不依赖于这个有问题的进程的其他进程。然而,完全的隔离过于严格,因为进程之间应该可以有意地进行交互;管道就是一个例子。因此,一个操作系统必须满足三个要求:多路复用、隔离和交互。

本章概述了操作系统如何组织以实现这三个要求。事实证明,有许多方法可以做到这一点,但本文集中在围绕单一内核(monolithic kernel)的主流设计上,许多Unix操作系统都采用这种设计。本章还概述了xv6进程,这是xv6中隔离的基本单元,以及xv6启动时第一个进程的创建。Xv6运行在多核的RISC-V微处理器上,其许多低级功能(例如,进程实现)特定于RISC-V。RISC-V是一种64位CPU,而xv6用“LP64”C语言编写,这意味着在C语言中,长整型(long)和指针(pointer)是64位的,而整型(int)是32位的。


Plus大约 17 分钟操作系统MITXV6riscv
Chapter 3 页表(Page tables)

​ 页表是操作系统为每个进程提供其独立的地址空间和内存的机制。页表决定了内存地址的意义以及哪些部分的物理内存可以被访问。它们使得xv6能够隔离不同进程的地址空间,并将它们多路复用到单个物理内存上。页表还提供了一种间接层,使得xv6可以执行一些技巧:在多个地址空间中映射相同的内存(例如跳板页),并通过未映射的页面来保护内核和用户栈。本章的其余部分将解释RISC-V硬件提供的页表以及xv6如何使用它们。

3.1 分页硬件(Paging hardware)

​ 提醒一下,RISC-V指令(包括用户和内核指令)操作虚拟地址。机器的RAM或物理内存是通过物理地址索引的。RISC-V的页表硬件通过将每个虚拟地址映射到一个物理地址来连接这两种地址。


Plus大约 22 分钟操作系统MITXV6riscv
XV6源码精读 -- vm.c

uvmcreate()

在操作系统中,每个进程都有自己的页表,用于管理其虚拟地址空间。当操作系统创建新的进程时,需要为该进程分配一个新的页表。

// 创建一个空的用户页表
// 如果没有内存了就返回0
pagetable_t
uvmcreate()
{
  pagetable_t pagetable;
  pagetable = (pagetable_t) kalloc();
  if(pagetable == 0)
    return 0;
  //将此页表都填充成0
  memset(pagetable, 0, PGSIZE);
  return pagetable;
}

Plus大约 2 分钟操作系统MITXV6riscvC
Lab 3: page tables

实验原文地址:Lab: page tables (mit.edu)

在这个实验中,你将探索页面表并修改它们以加速特定的系统调用,并检测哪些页面已被访问。

注意

在你开始编码之前,请阅读 xv6 书的第三章以及相关文件:

  • kernel/memlayout.h,其中包含内存布局的信息。
  • kernel/vm.c,其中包含大部分虚拟内存(VM)代码。
  • kernel/kalloc.c,其中包含分配和释放物理内存的代码。 同时,参考 RISC-V 特权架构手册可能也会有所帮助。

Plus大约 11 分钟操作系统MITXV6riscvC
XV6源码精读 -- proc.c

proc_pagetable(struct proc *p)

此函数的作用是创建一个新的用户页表,并在其中映射跳板代码和陷阱帧页。这个函数通常用于操作系统内核中创建进程时初始化进程的页表。

// 创建给定进程的用户页表,没有用户内存,但有跳板和陷阱帧页。
pagetable_t
proc_pagetable(struct proc *p)
{
  pagetable_t pagetable;

  // 创建一个空白页表
  pagetable = uvmcreate();
  if(pagetable == 0)
    return 0;

  //将跳板代码(用于系统调用返回)映射到最高的用户地址
  //只有内核使用该代码进出用户空间,所以不用设置PTE_U
  if(mappages(pagetable, TRAMPOLINE, PGSIZE,
              (uint64)trampoline, PTE_R | PTE_X) < 0){
    uvmfree(pagetable, 0);
    return 0;
  }

  // 将陷阱帧页映射到跳板页的下方,用于 trampoline.S
  if(mappages(pagetable, TRAPFRAME, PGSIZE,
              (uint64)(p->trapframe), PTE_R | PTE_W) < 0){
    uvmunmap(pagetable, TRAMPOLINE, 1, 0);
    uvmfree(pagetable, 0);
    return 0;
  }

  return pagetable;
}

Plus大约 2 分钟操作系统MITXV6riscvC