上下文切换
复习
- 程序如何变成进程:区分静态的程序文件和运行中的进程
- 进程的创建与结束:父子进程、退出状态和资源回收
- 进程控制块与进程状态:内核在哪里记录进程,以及三种状态的含义
TL;DR
- 上下文就是一个进程当前的运行现场:PC、寄存器、栈等
- 上下文切换:保存当前进程的现场,恢复下一个进程的现场
- 切换由内核完成,是实现“同时运行多个程序”的关键
- 切换本身不干“有用功”,还伴随额外开销,因此不宜过于频繁
正文
就绪队列里排着好几个进程,可 CPU 只有一个。让谁上?跑一会儿之后,又该怎么换人?
换人这件事的关键,在于记住上一个人做到哪了。
上下文是什么
一个进程正在运行时,它的全部“现场”都体现在 CPU 的状态里:
- PC 指向哪条指令
- 各个寄存器里是什么值
- 栈指针指向哪里
- 它正在用哪片内存
这些信息合起来,就叫进程的上下文(context)。只要把上下文原样恢复,进程就能像从没被打断过一样继续跑。
切换的三步
上下文切换(context switch)大致分三步:
- 保存:把当前进程的 PC、寄存器等,存进它的 PCB
- 选择:内核从就绪队列里挑出下一个要运行的进程
- 恢复:从那个进程的 PCB 里,把它的现场装回 CPU
做完这三步,CPU 上跑的就已经是另一个进程了。
进程 A 运行
│ 保存 A 的上下文到 A 的 PCB
│ 从就绪队列选中 B
│ 从 B 的 PCB 恢复 B 的上下文
▼
进程 B 运行
好比两个人接力做同一道题:第一个人做一半要走,先把“算到哪一步、草稿写了什么”记下来;第二个人接手时,照着记下的内容接着算。题没变,只是换了个脑子。
切换的开销
上下文切换让“多个程序同时运行”成为可能,但它并不便宜:
- 保存和恢复现场本身就要花时间,这段工夫不推进任何进程
- 切换会打乱 CPU 内部为当前进程做的优化,比如缓存和流水线
所以,切换太频繁会拖慢整体效率。好的调度,既要让大家都有机会跑,又要尽量减少无谓的切换。
什么时候切换
切换不会无缘无故发生,通常由这几件事引起:
- 时间片用完:一个进程跑了足够久,该让位了
- 主动阻塞:进程要等 I/O,主动让出 CPU
- 被更高优先级的进程打断:来了更要紧的事
到这里,进程“轮流上 CPU”的机制就通了。可到底该让谁先上、上多久?这属于操作系统的另一个核心话题——调度。下一章,我们先看看调度要解决哪些矛盾。
思考题
如果上下文切换不需要保存和恢复现场,直接让两个进程“混着跑”,会出什么问题?为什么“保存现场”是不可省略的一步?
小结
知识点
- 上下文是进程的运行现场:PC、寄存器、栈等
- 上下文切换:保存当前、选择下一个、恢复下一个
- 切换是分时多任务的基础,但有开销
- 切换常由时间片耗尽、阻塞或更高优先级打断引起
参考资料
- Wikipedia(zh):上下文切换:context switch
- Wikipedia(zh):进程控制块:现场的保存位置
思考题答案(仅供参考)
如果两个进程“混着跑”,它们会共用同一组寄存器和 PC:A 刚把一个中间结果放进寄存器,B 立刻把它覆盖掉;PC 也会在两条指令流之间乱跳。结果就是两边都算错。保存现场的意义,正是把每个进程的“进度”完整地存起来,互不覆盖;恢复现场,则是让它接着自己的进度继续。少了这一步,就没法把一个 CPU 安全地分给多个进程。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪