http://arthurchiao.art/blog/linux-cpu-2-zh/
1.1 调度器:时分复用 + 任务调度 —— sched
1.3 有任务:用哪个频率执行任务?—— cpufreq
1.4 无任务:执行轻量级占坑程序 —— idle task
从原理来说,非常简单。产品经理:什么都不做。
从实现来说,非常模糊。程序员:“什么都不做”的代码怎么写?
开发 leader 理解一下需求,从中翻译一下:
先保证一个目标:有任务变成 runnable 时(比如等到了键盘输入),能够恢复调度执行 —— 这决定了内核不能完全退出,比如至少要能主动或被动的响应系统事件;
在保证以上目标的前提下,内核做的事情越少越好 —— 节能减排,延迟处理器使用寿命,降本增效。
最终方案:引入一个特殊任务 idle task(很多资料中也叫 idle loop), 没有其他任务可调度时,就调度执行它。
从功能上来说,可以认为是一个优先级最低的占坑任务。
从实现来说,idle task 里面做什么都可以 —— 反正这时候这个 CPU 上没有任何其他 runnable tasks。 根据目的的不同,具体实现可以分为两大类:
节能;
低延迟。
1.4.1 直接降低电压和频率,节能
这是主流行为,idle task 里面实现某种降低功耗的逻辑,避免 CPU 空转,节能。 典型配置如 Linux 内核启动项 idle=halt。
这种方式的缺点是从较低功耗(某种程度的睡眠状态)唤醒时有一定的延迟。
1.4.1 直接降低电压和频率,节能
这是主流行为,idle task 里面实现某种降低功耗的逻辑,避免 CPU 空转,节能。 典型配置如 Linux 内核启动项 idle=halt。
这种方式的缺点是从较低功耗(某种程度的睡眠状态)唤醒时有一定的延迟。
1.4.2 仍然全速运行,保持最低唤醒延迟
这类场景比较特殊,比如追求极低延迟的高频交易场景。 没有任务时仍然让 CPU 保持电压和频率空转,不要降压降频, 这样有任务变成 runnable 时可以立即切换执行,延迟最低。 在 Linux 启动项中,对应 idle=poll 配置,后面几篇我们还会多次看到(尤其是这种配置的潜在风险)。
1.4.3 动态降低电压和频率,节能 —— cpuidle 和 c-states
通过一个单独的子系统(cpuidle)来实现不同级别的节能(c-states)。
这里注意和 turbo freq 的区别:
turbo 是部分 CORE 空闲时,有任务在运行的 CORE 可以动态超频, 目的是提高这几个有任务在运行的 CORE 的性能;
cpuidle/c-states 是当前 CORE/CPU 没有任务要运行(空闲 CPU),通过动态降频来节能。
1.5 idle loop 模式之三:空闲时间管理 —— cpuidle
再稍微展开介绍下上面第三种: 队列中如果没有 runnable task,比如所有任务都在等待 IO 事件。 这时候是没有任务需要 CPU 的,因此称为 CPU 空闲状态(idle states)。
空闲状态的下 CPU 该怎么管理,也是一门学问,因此内核又引入了另外一个子系统: cpu 空闲时间管理子系统 cpudile。具体工作内容后面介绍。
1.6 cpuidle + 响应延迟保证:电源管理服务等级 —— PM QoS
如果没有任务时 cpuidle 选择进入某种低电压/低频率的节能模式,当有任务到来时, 它的唤醒时间可能无法满足要求。针对这种情况,内核又引入了功耗管理或称电源管理 服务等级 (PM QoS)子系统。
PM QoS 允许应用注册一个最大 latency,内核确保唤醒时间不会高于这个阈值, 在尽量节能的同时实现快速响应。 具体原理也在后面单独章节介绍。
1.7 小结:各子系统的关系图
2 CPU 频率管理子系统(cpufreq):调节运行任务时的 p-state
2.1 原理:CPU performance/frequency scaling
3 linux idle loop
3.3 实现:idle loop
这里只是很简单的看一下,下一篇专门介绍内核实现。
简化之后,
while (1) {
while(!need_resched()) {
cpuidle_idle_call();
}
/*
[Note: Switch to a different task. We will return to this loop when the
idle task is again selected to run.]
*/
schedule_preempt_disabled();
}
如果没有其他任务,就执行 idle。从累积时间来说,idle 函数可能是人类历史上执行时间最长的函数。 [2]