为什么要使用操作系统
- 使用操作系统的主要原因是为了实现 CPU 多进程分时复用以及内存隔离
- 如果没有操作系统,应用程序会直接与硬件进行交互,这时应用程序会直接使用 CPU,比如假设只有一个 CPU 核,一个应用程序在这个 CPU 核上运行,但是同时其他程序也需要运行,因为没有操作系统来帮助切换,就需要应用程序时不时释放 CPU 资源,但是如果这个程序的某个函数有一个死循环,那它就永远也不会释放 CPU,甚至没办法做到运行第三方程序来停止或者杀死这个死循环程序,这种情况下就没办法实现 CPU 多进程的分时复用
- 还有从内存的角度来看,如果应用程序直接运行在硬件上,则程序的数据代码都直接保存到物理内存中,这样不同程序的内存之间没有明确边界,就可能会造成一个程序存储在本来属于另外一个程序的内存空间,覆盖另外一个程序中的内容
操作系统的隔离性需要隔离用户程序和操作系统,也需要隔离不同的进程
系统调用与隔离性
- 可以认为
exec
抽象了内存。当我们在执行 exec 系统调用的时候,我们会传入一个文件名,而这个文件名对应了一个应用程序的内存镜像。内存镜像里面包括了程序对应的指令,全局的数据。应用程序可以逐渐扩展自己的内存,但是应用程序并没有直接访问物理内存的权限,例如应用程序不能直接访问物理内存的 1000-2000 这段地址。不能直接访问的原因是,操作系统会提供内存隔离并控制内存,操作系统会在应用程序和硬件资源之间提供一个中间层。exec 是这样一种系统调用,它表明了应用程序不能直接访问物理内存。
-
files
基本上是抽象了磁盘。应用程序不会直接读取磁盘,在 Unix 中它与存储系统交互的唯一方式就是通过files
。操作系统会决定如何将文件与磁盘中的块对应,确保一个磁盘块只出现在一个文件中,并保证用户 A 不能操作用户 B 的文件。files 实现了不同用户之间以及同一用户不同进程之间的文件隔离。
硬件实现强隔离性
实现强隔离性的硬件支持包括了两部分:
-
user/kernel mode:处理器有两种操作模式:user mode 和 kernel mode:
- 当运行在 kernel model 时,CPU 能运行特定权限的指令(直接操作硬件的指令和设置保护的指令,如设置 page table 寄存器、关闭时钟中断)
- 当运行在 user mode 时,CPU 只能运行普通权限的指令
RISC-V 实际上有三种权限:user/kernel/machine mode
-
在 RISC-V 中,如果你在用户空间(user space)尝试执行一条特殊权限指令,用户程序会通过系统调用来切换到 kernel mode。当用户程序执行系统调用,会通过 ECALL 触发一个软中断(software interrupt),软中断会查询操作系统预先设定的中断向量表,并执行中断向量表中包含的中断处理程序。中断处理程序在内核中,这样就完成了 user mode 到 kernel mode 的切换,并执行用户程序想要执行的特殊权限指令
-
虚拟内存:每个进程都有自己独立的 page table,page table 将虚拟内存地址和物理内存地址做了对应
ECALL 指令
- 在 RISC-V 中,
ECALL
指令可以让用户程序将控制权转移给内核,并传入一个数字,这个数字表示了应用程序想要调用的 System Call -
ECALL
会跳转到内核中一个特定的位置,在内核侧,有一个位于syscall.c
的函数syscall
,每一个从应用程序发起的系统调用都会调用到这个syscall
函数,syscall
函数会检查ECALL
的参数
内核是如何夺回控制权
- 内核会通过硬件设置一个定时器,定时器到期之后会将控制权限从用户空间转移到内核空间,之后内核就有了控制能力并可以重新调度 CPU 到另一个进程中
宏内核 vs 微内核
宏内核
XV6 中,所有的操作系统服务都在 kernel mode 中,这种形式被称为宏内核
- 从安全的角度来说,这种方式不太好,在一个宏内核中,任何一个操作系统的 Bug 都有可能成为漏洞,如果有许多行代码运行在内核中,那么出现严重 Bug 的可能性也变得更大
- 宏内核的优势在于可以将文件系统、虚拟内存、进程管理这些实现特定功能的子模块紧密地集成在一起,这样可以提供很好的性能
微内核
微内核模式下,在内核中只有非常少的模块,将内核中的其他部分作为普通的用户程序来运行文章来源:https://www.toymoban.com/news/detail-855123.html
- 这样做的好处是内核中的代码数量较小,降低了 bug 出现的可能
- 如果用户程序想要使用内核的功能,但由于内核的程序作为普通的用户程序,比如说某个系统想要使用文件系统,需要完成从用户空间到内核空间,再从内核到用户空间来访问文件系统,文件系统也需要经过同样的路径将消息返回给用户系统,使得在微内核从用户到内核的跳转是宏内核的两倍,而反复跳转带来了性能的损耗,且微内核的内核程序被隔离开,难以实现共享,降低了系统的性能。
XV6 代码结构
代码主要由三部分组成:文章来源地址https://www.toymoban.com/news/detail-855123.html
-
kernel
:包含了基本上所有的内核文件 -
user
:这基本上是运行在 user mode 的程序 -
mkfs
:会创建一个空的文件镜像,将这个镜像存在磁盘上,就可以直接使用一个空的文件系统
kernel 编译过程
-
Makefile
读取一个 C 文件,比如proc.c
,然后调用 gcc 编译器,生成一个叫proc.s
的文件,这是 RISC-V 汇编语言文件,然后再走到汇编解释器,生成proc.o
,这是汇编语言的二进制格式,Makefile
会为所有内核文件做相同的操作 - 系统加载器收集所有的.o 文件,将它们链接在一起并生成内核文件
到了这里,关于MIT6.S081 - Lecture3: OS Organization and System Calls的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!