我总是读到/听说上下文切换的成本很高。我最近开始阅读 Robert Love 的“Linux 内核开发”,并完成了“进程和进程调度”一章。这让我对成本有了一些了解上下文切换不同进程和线程之间(因为线程被视为进程)。我想抓住这一点,真正了解成本上下文切换,理想情况下粗略地表示指令数量和损失的时间。
为了简单起见,我们假设一个单核处理器正在运行两个具有相同性能的进程善良加权(proc1 和 proc2)。另外,我们假设目标延迟是20ms,因此每个进程调度10ms。当一个上下文切换发生,我假设虽然上下文切换发生时,proc1 被挂起。此时proc2也暂停了?那么这是否意味着活动进程是某个内核线程或进程?如果是这样的话,这是否意味着 proc1 和 proc2 都无法在目标延迟?例如(数字仅用于演示):
+-----------20ms-----------+
|---proc1---|--|---proc2---|
^____9ms____^__^____9ms____^
|
2ms of kernel executing context_switch()
如果发生这种情况,上下文切换()从一个进程到另一个进程最终调用上下文切换()两次?一次从内核线程进入proc1
,然后另一个从内核线程进入proc2
?
答案1
有多少操作系统版本就有多少答案。没有一个答案可以涵盖一切。
基本上,上下文切换的成本是保存与进程上下文相关的所有 cpu 状态,然后加载到新进程的上下文中的成本。
到底保存什么不仅高度依赖于操作系统,还高度依赖于CPU硬件本身。例如,像 Intel cpu 这样的处理器有很多寄存器,必须保存在某个地方,然后用其他进程的上下文重新加载,而 sparc cpu 保留其大部分上下文,包括堆栈上的所有 cpu 寄存器,因此上下文切换是一个如果只是将堆栈指针移动到不同的寄存器窗口,这很重要。
另一方面,大多数现代 cpu 在 cpu 缓存内存中都有一些状态,虽然在上下文切换期间通常不会交换这些状态,但内存的使用可能会导致缓存行在新进程执行时被卸载和重新加载,因此当切换回前一个进程时,其缓存行可能需要重新加载。虽然这不是上下文切换的直接成本,但它仍然存在。
cpu中还有许多其他资源需要切换,例如页面映射、权限位等,并且每个cpu型号上的列表都不同,并且每个操作系统的处理方式也不同。
您引用了进程与线程。一度,它们之间存在巨大差异,线程需要切换的上下文要少得多。然后线程作为轻量级进程被创建,所以现在差别很小,进程几乎和线程一样轻。
此外,现代 CPU 专为多任务操作系统而设计,并包含试图减轻上下文切换成本的功能。但即使上下文切换成本更高,也有许多缓解措施。例如,一些 UNIX 在其调度中具有实时功能,可以区分交互式进程(需要在短时间内进行计算,然后等待用户交互)与具有最少 I/O 交互的纯计算线程。在这两种情况下,调度程序可以为交互式线程提供非常短的调度间隔以获得高优先级,同时为计算进程提供更长的间隔、较低的优先级和更少的上下文切换,以降低开销。
所以现代操作系统大多不使用固定长度的调度间隔,因为这不能适应工作负载。并且存在协作多任务处理(进程可以提前放弃其时间片)和基于时间的强制上下文切换的组合。
有些 cpu 甚至支持在不同内核上以不同速率进行调度,因此您可以拥有具有短时间片的交互式内核和具有较长时间片的计算驱动内核。
这是该主题复杂性的尖端,经过 50 多年的研究和开发,针对许多不同的工作负载和硬件功能对其进行了优化。关于这个主题已经写了整本书,这里的简短回答只能掩盖细节。