本文是自己在深入阅读Operating Systems Three Easy Pieces关于虚拟化CPU的知识点总结,后面会不断更新连续每一节,欢迎大家批评指正
0. overview
💡 为了能够虚拟化cpu,因此os需要实现一些机制来共享cpu→ time sharing,让一个程序运行一段时间,然后在切换到另外一个程序执行一段时间。存在一些挑战
- 性能需要控制好
- 需要保持对程序的控制,控制其运行时间和访问的数据 因此,在保持控制的同时获得高性能是构建操作系统的核心挑战之一。
1. Basic Technique: Limited Direct Execution
为了能够是程序像期望的一样执行,开发者提出了一种"有限直接执行:Limited Direct Execution"。
反过来"直接执行:Direct Execution" 指的是:直接将程序运行在cpu上
- 在进程链表中创建进程实体
- 为该进程分配内存
- 从disk将代码数据载入内存,并定位到函数执行入口(main),然后执行程序
- 执行完return后,返回到OS内核,并清除进程链表中的该进程
直接执行产生的问题:
- 若直接运行程序,OS如何限制程序执行,保证程序会按预期正常执行?
- 程序运行时,OS如何停止程序执行,并切换到其他程序上去?从而实现Time sharing.
2 直接执行引发的问题1: 限制操作
直接执行程序速度显然比较快,但若进程想执行 I/O 和其他一些的受限操作(如系统调用这些) ,或访问系统的资源,但又不能让进程完全控制系统。操作系统和硬件如何协同工作来做到这一点?
通过引入一种的处理器模式状态。
- user mode : 用户模式\用户态,限制用户的程序只能运行在该状态,不能进入系统内核执行,如不能直接执行I/O操作。但想执行咋办?→通过系统调用
- kernel mode:内核模式\内核态,在这种模式下,运行的代码可以做它喜欢的事情,包括特权操作,例如发出I / O请求和执行所有类型的受限制指令。
所以用户程序想要执行系统内核的代码,则需要通过系统调用 ,系统调用允许内核小心地向用户程序公开某些关键功能,例如访问文件系统等。
为了执行系统调用,需要两种特殊的指令:
- trap instruction,陷入指令:该指令同时跳转到内核并将权限级别提升到内核模式;一旦进入内核,系统现在可以按形式执行所需的任何特权操作,如I/O操作(如果允许),从而为调用进程执行所需的工作。
- return-from-trap instruction,从陷入指令返回指令。完成后,操作系统调用一个特殊的从陷阱返回指令,该指令返回到调用用户程序中,同时将特权级别降低回用户模式。
硬件在执行陷入指令时需要注意,因为它必须确保保存调用者寄存器的内容,以便在操作系统发出从陷阱返回指令时能够正确返回。
另一个问题:当程序调用系统调用时,该如何知道在OS哪里运行该系统调用的代码?
- 内核在启动的时候设置trap table 。当机器启动时,它以特权(内核)模式启动,因此可以根据需要自由配置机器硬件。因此,操作系统要做的第一件事就是告诉硬件在某些(trap指令时)异常事件发生时要运行什么代码。
- OS会告诉硬件这些陷入指令处理的硬件位置(通常是一些指令)。一旦通知硬件,它就会记住这些处理程序的位置,直到机器下次重新启动,因此硬件知道当系统调用和其他异常事件发生时要做什么(即跳转到什么代码)
- 为了标识 指定确切的系统调用,会给每个系统调用分配一个系统调用号码。用于标识每个系统调用。用户模式下调用系统调用时,会负责将系统调用号码放到 指定的寄存器或者堆栈的某个位置。
- 由OS系统调用的处理器来检查该系统调用号码,判断是否有效或者合法。
3 直接执行的问题2: 进程间的切换
直接执行的下一个问题是实现进程之间的切换。操作系统应该决定停止一个进程并启动另一个进程。
具体来说,如果一个进程在CPU上运行,根据定义,**这意味着操作系统没有运行。**如果操作系统没有运行,它怎么能做任何事情呢?(提示:它不能)虽然这听起来几乎是哲学上的,但这是一个真正的问题:如果OS不在CPU上运行,操作系统显然没有办法采取行动。
所以,操作系统如何重新获得对CPU的控制,以便它可以在进程之间切换?
3.1 A Cooperative Approach: Wait For System Calls
在协作调度系统中,操作系统通过等待程序系统调用 或某种非法操作的发生来重新获得对CPU的控制权。但是若有程序永远循环执行,且不调用系统调用,岂不是完蛋?
3.2 A Non-Cooperative Approach: The OS Takes Control
即使进程不合作,**操作系统如何获得对CPU的控制?**操作系统可以做些什么来确保流氓进程(无限执行)不会发生在机器上?→通过时间中断器
可以对定时器设备进行编程,使其每隔这么多毫秒就引发一次中断;当中断被引发时,当前运行的进程被暂停,并且操作系统中预先配置的中断处理程序运行 。此时,操作系统重新获得了对 CPU 的控制权,因此可以为所欲为:停止当前进程,并启动另一个进程。
一旦计时器开始,操作系统就可以感到安全,因为控制权最终将被归还给它,因此操作系统可以自由地运行用户程序。计时器也可以关闭(也是一种特权操作),稍后我们将在更详细地了解并发性时讨论。
请注意,当中断发生时,硬件有一些责任,特别是保存中断发生时正在运行的程序的状态,以便后续的从陷阱返回指令将能够正确恢复正在运行的程序。这组操作与硬件在显式系统调用陷阱进入内核期间的行为非常相似,因此可以保存各种寄存器(例如,保存到内核堆栈上),从而通过从陷阱返回的指令轻松恢复。
3.3 保存和恢复上下文
上下文切换在概念上很简单:操作系统所要做的就是为当前正在执行的进程保存一些寄存器值(例如,到其内核堆栈上)并为即将执行的进程恢复一些值(从它的内核堆栈)。
3.4 并发执行,中断怎么办?
当你处理一个中断而另一个中断发生时会发生什么?这在内核中不是很难处理吗?
- 在处理中断程序时,关中断,这样做可确保在处理一个中断时,不会向 CPU 提供其他中断。若关中断太长则会导致失去一些程序的中断
- 操作系统还开发了许多复杂的锁方案,以保护对内部数据结构的并发访问。这使得多个活动可以同时在内核中进行,这在多处理器上特别有用。
💡 因此,我们有了虚拟化CPU的基本机制。但是,一个主要问题尚未得到解答:我们应该在给定时间运行哪个进程?调度程序必须回答的正是这个问题,因此也是我们研究的下一个主题。