MIT6.S081 - Lecture3: OS Organization and System Calls

为什么要使用操作系统

  1. 使用操作系统的主要原因是为了实现 CPU 多进程分时复用以及内存隔离
  2. 如果没有操作系统,应用程序会直接与硬件进行交互,这时应用程序会直接使用 CPU,比如假设只有一个 CPU 核,一个应用程序在这个 CPU 核上运行,但是同时其他程序也需要运行,因为没有操作系统来帮助切换,就需要应用程序时不时释放 CPU 资源,但是如果这个程序的某个函数有一个死循环,那它就永远也不会释放 CPU,甚至没办法做到运行第三方程序来停止或者杀死这个死循环程序,这种情况下就没办法实现 CPU 多进程的分时复用
  3. 还有从内存的角度来看,如果应用程序直接运行在硬件上,则程序的数据代码都直接保存到物理内存中,这样不同程序的内存之间没有明确边界,就可能会造成一个程序存储在本来属于另外一个程序的内存空间,覆盖另外一个程序中的内容

操作系统的隔离性需要隔离用户程序和操作系统,也需要隔离不同的进程

系统调用与隔离性

  • 可以认为 exec 抽象了内存。当我们在执行 exec 系统调用的时候,我们会传入一个文件名,而这个文件名对应了一个应用程序的内存镜像。内存镜像里面包括了程序对应的指令,全局的数据。应用程序可以逐渐扩展自己的内存,但是应用程序并没有直接访问物理内存的权限,例如应用程序不能直接访问物理内存的 1000-2000 这段地址。不能直接访问的原因是,操作系统会提供内存隔离并控制内存,操作系统会在应用程序和硬件资源之间提供一个中间层。exec 是这样一种系统调用,它表明了应用程序不能直接访问物理内存。
  • files 基本上是抽象了磁盘。应用程序不会直接读取磁盘,在 Unix 中它与存储系统交互的唯一方式就是通过 files。操作系统会决定如何将文件与磁盘中的块对应,确保一个磁盘块只出现在一个文件中,并保证用户 A 不能操作用户 B 的文件。files 实现了不同用户之间以及同一用户不同进程之间的文件隔离

硬件实现强隔离性

实现强隔离性的硬件支持包括了两部分:

  1. user/kernel mode:处理器有两种操作模式:user mode 和 kernel mode:

    • 当运行在 kernel model 时,CPU 能运行特定权限的指令(直接操作硬件的指令和设置保护的指令,如设置 page table 寄存器、关闭时钟中断)
    • 当运行在 user mode 时,CPU 只能运行普通权限的指令

    RISC-V 实际上有三种权限:user/kernel/machine mode

  2. 在 RISC-V 中,如果你在用户空间(user space)尝试执行一条特殊权限指令,用户程序会通过系统调用来切换到 kernel mode。当用户程序执行系统调用,会通过 ECALL 触发一个软中断(software interrupt),软中断会查询操作系统预先设定的中断向量表,并执行中断向量表中包含的中断处理程序。中断处理程序在内核中,这样就完成了 user mode 到 kernel mode 的切换,并执行用户程序想要执行的特殊权限指令

  3. 虚拟内存:每个进程都有自己独立的 page table,page table 将虚拟内存地址和物理内存地址做了对应

ECALL 指令

  • 在 RISC-V 中,ECALL 指令可以让用户程序将控制权转移给内核,并传入一个数字,这个数字表示了应用程序想要调用的 System Call
  • ECALL 会跳转到内核中一个特定的位置,在内核侧,有一个位于 syscall.c 的函数 syscall,每一个从应用程序发起的系统调用都会调用到这个 syscall 函数,syscall 函数会检查 ECALL 的参数

内核是如何夺回控制权

  • 内核会通过硬件设置一个定时器,定时器到期之后会将控制权限从用户空间转移到内核空间,之后内核就有了控制能力并可以重新调度 CPU 到另一个进程中

宏内核 vs 微内核

宏内核

XV6 中,所有的操作系统服务都在 kernel mode 中,这种形式被称为宏内核

  • 从安全的角度来说,这种方式不太好,在一个宏内核中,任何一个操作系统的 Bug 都有可能成为漏洞,如果有许多行代码运行在内核中,那么出现严重 Bug 的可能性也变得更大
  • 宏内核的优势在于可以将文件系统、虚拟内存、进程管理这些实现特定功能的子模块紧密地集成在一起,这样可以提供很好的性能

微内核

微内核模式下,在内核中只有非常少的模块,将内核中的其他部分作为普通的用户程序来运行

  • 这样做的好处是内核中的代码数量较小,降低了 bug 出现的可能
  • 如果用户程序想要使用内核的功能,但由于内核的程序作为普通的用户程序,比如说某个系统想要使用文件系统,需要完成从用户空间到内核空间,再从内核到用户空间来访问文件系统,文件系统也需要经过同样的路径将消息返回给用户系统,使得在微内核从用户到内核的跳转是宏内核的两倍,而反复跳转带来了性能的损耗,且微内核的内核程序被隔离开,难以实现共享,降低了系统的性能。

XV6 代码结构

代码主要由三部分组成:

  1. kernel:包含了基本上所有的内核文件
  2. user:这基本上是运行在 user mode 的程序
  3. mkfs:会创建一个空的文件镜像,将这个镜像存在磁盘上,就可以直接使用一个空的文件系统

kernel 编译过程

  1. Makefile 读取一个 C 文件,比如 proc.c,然后调用 gcc 编译器,生成一个叫 proc.s 的文件,这是 RISC-V 汇编语言文件,然后再走到汇编解释器,生成 proc.o,这是汇编语言的二进制格式,Makefile 会为所有内核文件做相同的操作
  2. 系统加载器收集所有的.o 文件,将它们链接在一起并生成内核文件
posted @ 2024-04-20 10:58  jll133688  阅读(33)  评论(0编辑  收藏  举报