看看源头strace
我发现了克隆标志的使用CLONE_IDLETASK
,其描述为:
#define CLONE_IDLETASK 0x00001000 /* kernel-only flag */
深入研究后我发现,虽然该标志没有被涵盖,但man clone
实际上内核在启动过程中使用它为机器上的每个 CPU 创建空闲进程(所有这些进程的 PID 都应该为 0)。即一台有 8 个 CPU 的机器将至少有 7 个(见下面的问题)这样的进程“运行”(注意引号)。
现在,这让我产生了几个关于“空闲”进程实际上做了什么的问题。我的假设是它连续执行 NOP 操作,直到其时间范围结束并且内核分配一个真正的进程来运行或再次分配空闲进程(如果 CPU 没有被使用)。然而,这完全是猜测。所以:
在一台拥有 8 个 CPU 的机器上,会创建 7 个这样的空闲进程吗? (并且一个 CPU 将由内核本身占用,同时不执行用户空间工作?)
空闲进程真的只是无限的 NOP 操作流吗? (或执行相同操作的循环)。
CPU使用率(例如
uptime
)是否简单地通过空闲进程在CPU上运行的时间以及在特定时间段内空闲进程不存在的时间来计算?
PS 这个问题很可能很大程度上是由于我不完全理解CPU是如何工作的。即我了解汇编、时间范围和中断,但我不知道如何,例如,CPU 可能会根据其正在执行的内容使用更多或更少的能量。如果有人也能启发我,我将不胜感激。
答案1
空闲任务用于进程统计,同时也是为了减少能耗。在Linux中,为每个处理器创建一个空闲任务,并锁定到该处理器;只要该 CPU 上没有其他进程运行,就会调度空闲任务。花费在空闲任务上的时间在诸如top
. (正常运行时间的计算方式不同。)
Unix 似乎总是有某种空闲循环(但不一定是实际的空闲任务,请参阅吉尔斯的回答),甚至在 V1 中它使用了一条WAIT
指令它停止处理器直到发生中断(它代表“等待中断”)。其他一些操作系统使用繁忙循环,DOS,操作系统/2,尤其是 Windows 的早期版本。长期以来,CPU 一直使用这种“等待”指令来减少能耗和热量产生。您可以看到空闲任务的各种实现,例如arch/x86/kernel/process.c
在Linux内核中:基本的只是调用HLT
,它会停止处理器直到发生中断(并启用 C1 节能模式),其他实现处理各种错误或效率低下(例如使用MWAIT
而不是HLT
在某些 CPU 上)。
当进程等待事件(I/O 等)时,所有这些都与进程中的空闲状态完全分开。
答案2
在进程调度程序的教科书设计中,如果调度程序没有任何进程要调度(即,如果所有进程都被阻塞,等待输入),则调度程序将等待处理器中断。中断可以指示来自外围设备的输入(用户操作、网络数据包、从磁盘完成的读取等),或者可以是触发进程中的计时器的计时器中断。
Linux 的调度程序没有针对无事可做情况的特殊代码。相反,它将无事可做的情况编码为特殊进程,即空闲进程。仅当没有其他进程可调度时,空闲进程才会被调度(它实际上具有无限低的优先级)。空闲进程实际上是内核的一部分:它是一个内核线程,即执行内核中代码的线程,而不是进程中的代码。 (更准确地说,每个 CPU 都有一个这样的线程。)当空闲进程运行时,它会执行等待中断操作。
等待中断的工作方式取决于处理器的功能。对于最基本的处理器设计,这只是一个繁忙的循环 -
nothing:
goto nothing
处理器永远运行分支指令,但什么也做不了。大多数现代操作系统不会这样做,除非它们运行在没有更好的处理器上,而大多数处理器都有更好的东西。理想情况下,处理器应该关闭,而不是除了给房间供暖之外什么也不做。因此,内核运行代码来指示处理器自行关闭,或者至少关闭大部分处理器。必须至少有一小部分保持通电状态,即中断控制器。当外设触发中断时,中断控制器将向主(部分)处理器发送唤醒信号。
实际上,Intel/AMD 和 ARM 等现代 CPU 具有许多复杂的电源管理设置。操作系统可以估计处理器将保持空闲模式的时间,并根据此选择不同的低功耗模式。这些模式在空闲时的功耗以及进入和退出空闲模式所需的时间之间提供了不同的折衷方案。在某些处理器上,当操作系统发现进程没有消耗太多 CPU 时间时,它还可以降低处理器的时钟速率。
答案3
不,空闲任务不会浪费 CPU 周期。调度程序根本不选择空闲进程来执行。空闲进程正在等待某个事件发生,以便它可以继续。例如,它可以等待read()
系统调用中的输入。
顺便说一句,内核不是一个单独的进程。内核代码总是在进程的上下文中执行(当然,除了内核线程的特殊情况),因此“一个 CPU 将由内核本身占用,同时不执行用户空间工作”的说法是不正确的。