MIT6.S081
  • 简介
  • Lec01 Introduction and Examples (Robert)
    • 1.1 课程内容简介
    • 1.2 操作系统结构
    • 1.3 Why Hard and Interesting
    • 1.4 课程结构和资源
    • 1.5 read, write, exit系统调用
    • 1.6 open系统调用
    • 1.7 Shell
    • 1.8 fork系统调用
    • 1.9 exec, wait系统调用
    • 1.10 I/O Redirect
  • Lec03 OS Organization and System Calls (Frans)
    • 3.1 上一节课回顾
    • 3.2 操作系统隔离性(isolation)
    • 3.3 操作系统防御性(Defensive)
    • 3.4 硬件对于强隔离的支持
    • 3.5 User/Kernel mode切换
    • 3.6 宏内核 vs 微内核 (Monolithic Kernel vs Micro Kernel)
    • 3.7 编译运行kernel
    • 3.8 QEMU
    • 3.9 XV6 启动过程
  • Lec04 Page tables (Frans)
    • 4.1 课程内容简介
    • 4.2 地址空间(Address Spaces)
    • 4.3 页表(Page Table)
    • 4.4 页表缓存(Translation Lookaside Buffer)
    • 4.5 Kernel Page Table
    • 4.6 kvminit 函数
    • 4.7 kvminithart 函数
    • 4.8 walk 函数
  • Lec05 Calling conventions and stack frames RISC-V (TA)
    • 5.1 C程序到汇编程序的转换
    • 5.2 RISC-V vs x86
    • 5.3 gdb和汇编代码执行
    • 5.4 RISC-V寄存器
    • 5.5 Stack
    • 5.6 Struct
  • Lec06 Isolation & system call entry/exit (Robert)
    • 6.1 Trap机制
    • 6.2 Trap代码执行流程
    • 6.3 ECALL指令之前的状态
    • 6.4 ECALL指令之后的状态
    • 6.5 uservec函数
    • 6.6 usertrap函数
    • 6.7 usertrapret函数
    • 6.8 userret函数
  • Lec08 Page faults (Frans)
    • 8.1 Page Fault Basics
    • 8.2 Lazy page allocation
    • 8.3 Zero Fill On Demand
    • 8.4 Copy On Write Fork
    • 8.5 Demand Paging
    • 8.6 Memory Mapped Files
  • Lec09 Interrupts (Frans)
    • 9.1 真实操作系统内存使用情况
    • 9.2 Interrupt硬件部分
    • 9.3 设备驱动概述
    • 9.4 在XV6中设置中断
    • 9.5 UART驱动的top部分
    • 9.6 UART驱动的bottom部分
    • 9.7 Interrupt相关的并发
    • 9.8 UART读取键盘输入
    • 9.9 Interrupt的演进
  • Lec10 Multiprocessors and locking (Frans)
    • 10.1 为什么要使用锁?
    • 10.2 锁如何避免race condition?
    • 10.3 什么时候使用锁?
    • 10.4 锁的特性和死锁
    • 10.5 锁与性能
    • 10.6 XV6中UART模块对于锁的使用
    • 10.7 自旋锁(Spin lock)的实现(一)
    • 10.8 自旋锁(Spin lock)的实现(二)
  • Lec11 Thread switching (Robert)
    • 11.1 线程(Thread)概述
    • 11.2 XV6线程调度
    • 11.3 XV6线程切换(一)
    • 11.4 XV6线程切换(二)
    • 11.5 XV6进程切换示例程序
    • 11.6 XV6线程切换 --- yield/sched函数
    • 11.7 XV6线程切换 --- switch函数
    • 11.8 XV6线程切换 --- scheduler函数
    • 11.9 XV6线程第一次调用switch函数
  • Lec13 Sleep & Wake up (Robert)
    • 13.1 线程切换过程中锁的限制
    • 13.2 Sleep&Wakeup 接口
    • 13.3 Lost wakeup
    • 13.4 如何避免Lost wakeup
    • 13.5 Pipe中的sleep和wakeup
    • 13.6 exit系统调用
    • 13.7 wait系统调用
    • 13.8 kill系统调用
  • Lec14 File systems (Frans)
    • 14.1 Why Interesting
    • 14.2 File system实现概述
    • 14.3 How file system uses disk
    • 14.4 inode
    • 14.5 File system工作示例
    • 14.6 XV6创建inode代码展示
    • 14.7 Sleep Lock
  • Lec15 Crash recovery (Frans)
    • 15.1 File system crash概述
    • 15.2 File system crash示例
    • 15.3 File system logging
    • 15.4 log_write函数
    • 15.5 end_op函数
    • 15.6 File system recovering
    • 15.7 Log写磁盘流程
    • 15.8 File system challenges
  • Lec16 File system performance and fast crash recovery (Robert)
    • 16.1 Why logging
    • 16.2 XV6 File system logging回顾
    • 16.3 ext3 file system log format
    • 16.4 ext3如何提升性能
    • 16.5 ext3文件系统调用格式
    • 16.6 ext3 transaction commit步骤
    • 16.7 ext3 file system恢复过程
    • 16.8 为什么新transaction需要等前一个transaction中系统调用执行完成
    • 16.9 总结
  • Lec17 Virtual memory for applications (Frans)
    • 17.1 应用程序使用虚拟内存所需要的特性
    • 17.2 支持应用程序使用虚拟内存的系统调用
    • 17.3 虚拟内存系统如何支持用户应用程序
    • 17.4 构建大的缓存表
    • 17.5 Baker's Real-Time Copying Garbage Collector
    • 17.6 使用虚拟内存特性的GC
    • 17.7 使用虚拟内存特性的GC代码展示
  • Lec18 OS organization (Robert)
    • 18.1 Monolithic kernel
    • 18.2 Micro kernel
    • 18.3 Why micro kernel?
    • 18.4 L4 micro kernel
    • 18.5 Improving IPC by Kernel Design
    • 18.6 Run Linux on top of L4 micro kernel
    • 18.7 L4 Linux性能分析
  • Lec19 Virtual Machines (Robert)
    • 19.1 Why Virtual Machine?
    • 19.2 Trap-and-Emulate --- Trap
    • 19.3 Trap-and-Emulate --- Emulate
    • 19.4 Trap-and-Emulate --- Page Table
    • 19.5 Trap-and-Emulate --- Devices
    • 19.6 硬件对虚拟机的支持
    • 19.7 Dune: Safe User-level Access to Privileged CPU Features
  • Lec20 Kernels and HLL (Frans)
    • 20.1 C语言实现操作系统的优劣势
    • 20.2 高级编程语言实现操作系统的优劣势
    • 20.3 高级编程语言选择 --- Golang
    • 20.4 Biscuit
    • 20.5 Heap exhaustion
    • 20.6 Heap exhaustion solution
    • 20.7 Evaluation: HLL benefits
    • 20.8 Evaluation: HLL performance cost(1)
    • 20.9 Evaluation: HLL performance cost(2)
    • 20.10 Should one use HLL for a new kernel?
  • Lec21 Networking (Robert)
    • 21.1计算机网络概述
    • 21.2 二层网络 --- Ethernet
    • 21.3 二/三层地址转换 --- ARP
    • 21.4 三层网络 --- Internet
    • 21.5 四层网络 --- UDP
    • 21.6 网络协议栈(Network Stack)
    • 21.7 Ring Buffer
    • 21.8 Receive Livelock
    • 21.9 如何解决Livelock
  • Lec22 Meltdown (Robert)
    • 22.1 Meltdown发生的背景
    • 22.2 Speculative execution(1)
    • 22.3 Speculative execution(2)
    • 22.4 CPU caches
    • 22.5 Flush and Reload
    • 22.6 Meltdown Attack
    • 22.7 Meltdown Fix
  • Lec23 RCU (Robert)
    • 23.1 使用锁带来的问题
    • 23.2 读写锁 (Read-Write Lock)
    • 23.3 RCU实现(1) - 基本实现
    • 23.4 RCU实现(2) - Memory barrier
    • 23.5 RCU实现(3) - 读写规则
    • 23.6 RCU用例代码
    • 23.7 RCU总结
Powered by GitBook
On this page

Was this helpful?

  1. Lec08 Page faults (Frans)

8.1 Page Fault Basics

PreviousLec08 Page faults (Frans)Next8.2 Lazy page allocation

Last updated 4 years ago

Was this helpful?

今天的课程内容是page fault,以及通过page fault可以实现的一系列虚拟内存功能。这里相关的功能有:

  • lazy allocation,这是下一个lab的内容

  • copy-on-write fork

  • demand paging

  • memory mapped files

你懂的,几乎所有稍微正经的操作系统都实现了这些功能。比如Linux就实现了所有的这些功能。然而在XV6中,实话实说,一个这样的功能都没实现。在XV6中,一旦用户空间进程触发了page fault,会导致进程被杀掉。这是非常保守的处理方式。

在这节课,我们将会探讨在发生page fault时可以做的一些有趣的事情。这节课对于代码的讲解会比较少,相应的在设计层面会有更多的内容,毕竟我们也没有代码可以讲解(因为XV6中没有实现)。

另一件重要的事情是,今天课程的内容对应了后面几个实验。下一个实验lazy lab今天会发布出来,copy-on-write fork和mmap也是后续实验的内容。这些都是操作系统中非常有趣的部分,我们将会在实验中花大量时间来研究它。

在进入到具体细节之前,我们先来简单回顾一下虚拟内存。你可以认为虚拟内存有两个主要的优点:

  • 第一个是Isolation,隔离性。虚拟内存使得操作系统可以为每个应用程序提供属于它们自己的地址空间。所以一个应用程序不可能有意或者无意的修改另一个应用程序的内存数据。虚拟内存同时也提供了用户空间和内核空间的隔离性,我们在之前的课程已经谈过很多相关内容,并且你们通过page table lab也可以理解虚拟内存的隔离性。

  • 另一个好处是level of indirection,提供了一层抽象。处理器和所有的指令都可以使用虚拟地址,而内核会定义从虚拟地址到物理地址的映射关系。这一层抽象是我们这节课要讨论的许多有趣功能的基础。不过到目前为止,在XV6中内存地址的映射都比较无聊,实际上在内核中基本上是直接映射(注,也就是虚拟地址等于物理地址)。当然也有几个比较有意思的地方:

    • trampoline page,它使得内核可以将一个物理内存page映射到多个用户地址空间中。

    • guard page,它同时在内核空间和用户空间用来保护Stack。

到目前为止,我们介绍的内存地址映射相对来说比较静态。不管是user page table还是kernel page table,都是在最开始的时候设置好,之后就不会再做任何变动。

page fault可以让这里的地址映射关系变得动态起来。通过page fault,内核可以更新page table,这是一个非常强大的功能。因为现在可以动态的更新虚拟地址这一层抽象,结合page table和page fault,内核将会有巨大的灵活性。我们接下来会看到各种各样利用动态变更page table实现的有趣的功能。

但是在那之前,首先,我们需要思考的是,什么样的信息对于page fault是必须的。或者说,当发生page fault时,内核需要什么样的信息才能够响应page fault。

  • 很明显的,我们需要出错的虚拟地址,或者是触发page fault的源。可以假设的是,你们在page table lab中已经看过一些相关的panic,所以你们可能已经知道,当出现page fault的时候,XV6内核会打印出错的虚拟地址,并且这个地址会被保存在STVAL寄存器中。所以,当一个用户应用程序触发了page fault,page fault会使用与Robert教授上节课介绍的相同的trap机制,将程序运行切换到内核,同时也会将出错的地址存放在STVAL寄存器中。这是我们需要知道的第一个信息。

  • 我们需要知道的第二个信息是出错的原因,我们或许想要对不同场景的page fault有不同的响应。不同的场景是指,比如因为load指令触发的page fault、因为store指令触发的page fault又或者是因为jump指令触发的page fault。所以实际上如果你查看RISC-V的文档,在SCAUSE(注,Supervisor cause寄存器,保存了trap机制中进入到supervisor mode的原因)寄存器的介绍中,有多个与page fault相关的原因。比如,13表示是因为load引起的page fault;15表示是因为store引起的page fault;12表示是因为指令执行引起的page fault。所以第二个信息存在SCAUSE寄存器中,其中总共有3个类型的原因与page fault相关,分别是读、写和指令。ECALL进入到supervisor mode对应的是8,这是我们在上节课中应该看到的SCAUSE值。基本上来说,page fault和其他的异常使用与系统调用相同的trap机制(注,详见lec06)来从用户空间切换到内核空间。如果是因为page fault触发的trap机制并且进入到内核空间,STVAL寄存器和SCAUSE寄存器都会有相应的值。

  • 我们或许想要知道的第三个信息是触发page fault的指令的地址。从上节课可以知道,作为trap处理代码的一部分,这个地址存放在SEPC(Supervisor Exception Program Counter)寄存器中,并同时会保存在trapframe->epc(注,详见lec06)中。

所以,从硬件和XV6的角度来说,当出现了page fault,现在有了3个对我们来说极其有价值的信息,分别是:

  • 引起page fault的内存地址

  • 引起page fault的原因类型

  • 引起page fault时的程序计数器值,这表明了page fault在用户空间发生的位置

我们之所以关心触发page fault时的程序计数器值,是因为在page fault handler中我们或许想要修复page table,并重新执行对应的指令。理想情况下,修复完page table之后,指令就可以无错误的运行了。所以,能够恢复因为page fault中断的指令运行是很重要的。

接下来我们将查看不同虚拟内存功能的实现机制,来帮助我们理解如何利用page fault handler修复page table并做一些有趣的事情。