一、一个操作系统的基本要求
一个操作系统至少需要满足以下三个要求:
- 多路复用
- 进程隔离
- 进程通信
(一)多路复用
硬件CPU的数量有限,且往往少于同时存在的进程数量。而操作系统需要支持进程的并发执行,所以操作系统应该能使多个进程分时共享计算机的资源。
(二)进程隔离
一个进程的运行,应当具有一定的独立性,这个独立性指该进程在一定程度上不受其他进程的影响。这可以保证出了bug的程序不会严重影响其他程序的正常运行。
(三)进程通信
如上所说,进程的隔离并非是完全的,而是一定程度上的。进程间可能需要通信来协调进程的执行。
二、操作系统隔离了应用程序与硬件资源
倘若应用程序与硬件资源直接交互,并把之前的系统调用视为一个库,应用程序与之连接。这样做,应用程序可能会实现高效、可预测的性能。一些用于嵌入式设备或实时系统的操作系统就是采用这样的方式。
但缺点就是,多个应用程序间必须配合的天衣无缝。比如每个应用程序必须定期放弃CPU,以使其他应用程序可以运行,这样做要求所有应用程序间都相互信任且没有bug。但这往往是难以实现的,所以操作系统实现应用程序与硬件资源的强隔离是非常有必要的。
操作系统实现强隔离的方式是将资源抽象为服务。比如Unix应用程序只通过文件系统的open、write等系统调用去与文件系统直接交互,而不是直接读写磁盘,这带来了路径名的便利。
总得来说,操作系统强隔离了应用程序与硬件资源,将资源抽象为服务,提高了开发与程序执行的效率。
三、实现强隔离的硬件支持——RISC-V的三种模式
RISC-V指令集架构的CPU有三种模式,分别是机器模式、监督者模式和用户模式。
- 一个CPU在机器模式下启动,因为机器模式下执行的指令具有完全的权限。机器模式主要用于配置计算机。xv6在机器模式下执行几条指令后就会转为监督者模式。
- 在监督者模式下,CPU被允许执行特权指令,例如:启用和禁用中断、读写保存页表地址的寄存器。处于监督者模式的软件可以执行特权指令,被称为运行在内核空间。运行在内核空间(或称监督者模式)的软件被称为内核。
- 应用程序只能运行在用户模式,若应用程序试图执行一条特权指令,CPU不会去执行它,而是使用ecall指令,将CPU从用户模式切换到监督者模式,并在内核指定的入口处进入内核。监督者模式(内核)的代码会终止该应用程序,并验证系统调用的参数,决定是否允许应用程序执行请求的操作,然后拒绝或执行该操作。
注意:内核控制监督者模式的入口点,如果是应用程序决定内核的入口点,那么恶意程序就能够在跳过参数验证的情况下进入内核。
四、xv6操作系统的设计结构(内核组织)——宏内核
(一)宏内核与微内核
宏内核与微内核是内核的两种组织方式。
宏内核:又名单内核。它的用户服务与内核服务都保存在相同的地址空间,有内核进行统一管理。
微内核:用户服务与内核服务保存在不同的地址空间。
如下图所示:
在宏内核中,内核是一个大的整体,一个大的进程,函数间的调用链路少,之间通信简单高效。
在微内核中,微内核的功能被划分为独立的进程,进程间通过IPC通信,高度模块化,一个服务的故障不会影响另一个服务。但由于模块化,函数之间调用链路偏长,进程间不会之间通信,而是通过内核服务相互通信。
宏内核的执行效率高于微内核,因为微内核涉及到跨模块调用。但宏内核的扩展性比微内核差。
(二)xv6的宏内核组织
xv6采用宏内核组织,其所有系统调用都在监督者模式下运行。整个操作系统以全硬件权限运行。
这导致xv6的开发者需要仔细地琢磨各个接口,一旦有一个错误就是致命的。因为监督者模式下的错误往往会导致内核崩溃,内核崩溃导致计算机停止工作,所有应用程序崩溃,计算机必须重启。
xv6的内核源码在kernel/子目录下。按照模块化的设计思想,源码被分为了多个文件,下图列出了这些文件及其功能。
文件名 | 功能描述 |
bio.c | 文件系统的磁盘块缓存 |
console.c | 连接用户键盘与屏幕 |
exec.c | exec系统调用 |
file.c | 文件描述符的支持文件 |
fs.c | 文件系统 |
kalloc.c | 物理页面分配程序 |
log.c | 文件系统记录与崩溃恢复 |
main.c | 在启动过程中控制其他模块的初始化 |
pipe.c | 管道 |
plic.c | RISC-V中断控制器 |
printf.c | 到控制台的格式化输出 |
proc.c | 进程和调度 |
sleeplock.c | 自锁并让出CPU |
spinlock.c | 自锁但不让出CPU |
start.c | 机器模式启动代码 |
string.c | C 字符串和字节数组库 |
syscall.c | 分派系统调用处理函数 |
sysfile.c | 文件相关系统调用 |
sysproc.c | 进程相关系统调用 |
trap.c | 陷阱和中断的响应返回C程序 |
uart.c | 串行端口控制台设备驱动程序 |
virtio_disk.c | 磁盘驱动程序 |
vm.c | 管理页表和地址空间 |
entry.S | 首次开机引导程序 |
kernelvec.S | 内核陷阱和计时器中断的处理程序 |
swtch.S | 线程切换 |
trampoline.S | 用于切换用户空间与内核空间的汇编代码 |
五、xv6的进程实现
xv6的隔离单位是一个进程。进程的实现,可以防止一个进程破坏或监视另一个进程的内存、CPU、文件描述符等。同时还可以防止进程破坏