目录
1. 虚拟化CPU
2. 进程
2.1 进程的机器状态
2.2 进程创建
2.3 进程的状态
3. 受限直接执行
3.1 直接执行
3.2 受限制的操作
3.3 在进程之间切换
3.3.1 协作方式:等待系统调用
3.3.2 非协作方式:操作系统进行控制
3.3.3 保存和恢复上下文
3.3.4 在系统调用期间发生时钟中断
Operating Systems: Three Easy Pieces 笔记
第4章 抽象:进程
第6章 机制:受限直接执行
时分共享(time sharing)CPU 技术:通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟 CPU 的假象。
要实现 CPU 的虚拟化,操作系统就需要一些机制(mechanism)和策略(policy)。
机制是一些低级方法或协议,例如,
上下文切换(context switch)让操作系统能够停止运行一个程序,并开始在给定的 CPU 上运行另一个程序。
策略是在操作系统内做出某种决定的算法,例如,
调度策略(scheduling policy)利用历史信息(例如,哪个程序在最后一分钟运行得更多?)、工作负载知识(例如,运行什么类型的程序?)以及性能指标 (例如,系统是否针对交互式性能或吞吐量进行优化?)决定运行哪个程序
进程就是运行中的程序。
进程的机器状态(machine state):程序在运行时可以读取或更新的内容。
在任何时刻,机器的哪些部分对执行该程序很重要?
内存。
指令存在内存中。正在运行的程序读取和写入的数据也在内存中。
因此进程可以访问的内存(称为地址空间,address space)是该进程的一部分。
寄存器。
许多指令明确地读取或更新寄存器。
有一些非常特殊的寄存器构成了该机器状态的一部分。例如,
程序计数器(Program Counter,PC)(有时称为指令指针,Instruction Pointer 或 IP)告诉我们程序当前正在执行哪个指令;
栈指针(stack pointer)和相关的帧指针(frame pointer)用于管理函数参数栈、局部变量和返回地址。
将代码和静态数据(例如初始化变量)加载到内存中
为程序的运行时栈分配一些内存(存放局部变量、函数参数和返回地址)
执行与 I/O 设置相关的任务
UNIX 每个进程 3 个打开的文件描述符(file descriptor),用于标准输入、输出和错误。
启动程序,在入口处运行,即 main()
运行(running)
就绪(ready)
阻塞(blocked):该状态下,进程执行了某种操作,直到发生其它事件时才会准备运行,例如
当进程向磁盘发起 I/O 请求时,它会被阻塞,因此其它进程可以使用处理器。
初始(initial):表示进程在创建时处于的状态。
最终(final):表示进程处于已退出但尚未清理的状态(僵尸状态)。
虚拟化CPU的挑战
性能:如何在不增加系统开销的情况下实现虚拟化?
控制权:如何有效地运行进程,同时保留对 CPU 的控制?
受限的直接执行(limited direct execution)
直接执行 -> 只需直接在 CPU上运行程序即可
当 OS 启动程序运行时,它会在进程列表中为其创建一个进程条目,为其分配一些内存,将程序代码(从磁盘)加载到内存中,找到入口点,例如main()函数,并开始运行用户的代码。表 6.1 展示了这种基本的直接执行协议(没有任何限制),使用正常的调用并返回跳转到程序的 main(),并在稍后回到内核。
存在的问题
第一,如果我们只运行一个程序,操作系统怎么能确保它不做任何我们不希望它做的事
第二,当我们运行一个进程时,操作系统如何让它停下来并切换到另一个进程
直接执行的明显优势是快速。
如果进程希望执行某种受限操作(如向磁盘发出 I/O 请求或获得更多系统资源),该怎么办?
=> 采用受保护的控制权转移
硬件通过提供不同的执行模式来协助操作系统。在用户模式(user mode)下,应用程序不能完全访问硬件资源。在内核模式(kernel mode)下,操作系统可以访问机器的全部资源。还提供了陷入(trap)内核和从陷阱返回(return-from-trap)到用户模式程序的特别说明,以及一些指令,让操作系统告诉硬件陷阱表(trap table)在内存中的位置。
在协作调度系统中,OS 通过等待系统调用,或某种非法操作发生,从而重新获得 CPU 的控制权。在这种风格下,操作系统相信系统的 进程会合理运行。运行时间过长的进程被假定会定期放弃 CPU,以便操作系统可以决定运 行其他任务。
如何在没有协作的情况下获得控制权? -> 时钟中断(timer interrupt)
时钟设备可以编程为每隔几毫秒产生一次中断。产生中断时,当前正在运行的进 程停止,操作系统中预先配置的中断处理程序(interrupt handler)会运行。此时,操作系统 重新获得 CPU 的控制权,因此可以做它想做的事:停止当前进程,并启动另一个进程。
上下文切换:操作系统为当前正在执行的进程保存一些寄存器 的值(例如,到它的内核栈),并为即将执行的进程恢复一些寄存器的值(从它的内核栈)。 这样一来,操作系统就可以确保最后执行从陷阱返回指令时,不是返回到之前运行的进程, 而是继续执行另一个进程。
上表的时间线说明:
进程 A 正在运行,然后被中断时钟中断。硬件保存它的寄存器(在内核栈中),并进入内核(切换到内核模式)。
在时钟中断处理程序中,操作系统决定从正在运行的进程 A 切换到进程 B。此时,它调用 switch()例程, 该例程仔细保存当前寄存器的值(保存到 A 的进程结构),恢复寄存器进程 B(从它的进程结构),然后切换上下文(switch context),具体来说是通过改变栈指针来使用 B 的内核栈
最后,操作系统从陷阱返回,恢复 B 的寄存器并开始运行它。
操作系统可能简单地决定,在中断处理期间禁止中断(disable interrupt)。这样做可以确保在处理一 个中断时,不会将其他中断交给 CPU。