CPU 空闲本身是个决策问题:该进哪个 idle state 睡?深睡省电但唤醒慢;浅睡反应快但耗。选错了影响功耗甚至响应延迟。Linux 把这块抽成 cpuidle 子系统,governor 插件决定”睡多深”。
一、先看图
flowchart LR
CPU[CPU idle loop] --> G{cpuidle governor}
G -->|menu / teo 决策| S0[C0 running]
G --> C1[C1 halt<br/>~1μs]
G --> C2[C2<br/>~10μs]
G --> C3[C3<br/>~50μs]
G --> C6[C6<br/>~200μs 关 core]
G --> C7[C8/C10<br/>关 package/uncore]
S0 -.事件唤醒.-> WK[wake → 恢复]
C1 -.-> WK
C6 -.-> WK
classDef low fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef mid fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef deep fill:#a371f722,stroke:#a371f7,color:#adbac7;
class S0,C1 low
class C2,C3 mid
class C6,C7 deep
C-state 越大:功耗越低、退出延迟越大。choice 权衡”预期 idle 时间”。
二、cpuidle 架构
三个模块:
- 驱动(
intel_idle、acpi_idle、arm_idle):知道本 CPU 支持哪些 C-state,每个 target residency 与 exit latency - governor:决策 “下次睡多久、睡哪级”
- 通用层:
cpuidle_enter_state(i)真正进入
/sys/devices/system/cpu/cpu0/cpuidle/state0..N/:
name # POLL / C1 / C1E / C6 / ...
desc
latency # us
residency # us, minimum time in this state to be worth it
usage # 进入次数
time # 累计停留 μs
三、governor 演进
3.1 ladder
老 governor,单调上 / 下一级。简单,在多数服务器过时。
3.2 menu
从 3.x 到现在长期默认。预测下次 idle 时长基于:
- 历史统计(指数衰减)
- 下一个 timer 到期时间
- 当前 I/O pending
选择 target_residency < predicted
的最深状态。
痛点:对突发不规则唤醒不敏感;某些负载省电不够。
3.3 teo(Timer Events Oriented)
5.1+ 引入,现代默认候选。思路更单纯:“相信 timer 预测”。统计每个 C-state 的 命中率(实际停留时间 >= target_residency 的比例)。进入前选命中率 > 50% 且延迟最深的。
TEO 在交互 / 服务器混合负载上表现更稳定。Fedora、Ubuntu
较新版本默认
teo(/sys/devices/system/cpu/cpuidle/current_governor_ro)。
3.4 haltpoll
专为 KVM guest 设计:先 busy-poll 一会儿(halt-polling),若无事件再真正 halt。降低虚拟机空闲-唤醒延迟。
四、C-state 能耗地图(Intel 参考)
- C0:running
- C1:HLT,停指令执行,L1/L2 保留。退出 ~1μs
- C1E:C1 + 降电压
- C3:L2 刷新;50μs 级退出
- C6:core power gate;200μs;显著省电
- C7/C8/C10:package / uncore C-state。关整个 socket 的 cache、PCIe link。退出几 ms
- PC6 / PC10:package C-state——所有 core 都 C6/C10 才能进
服务器通常不进 PC10(影响 I/O 延迟);笔记本 PC10 很激进。
ARM 类似:WFI(Wait For Interrupt)对应 C1;更深的 cluster idle 关 L2。
五、uncore idle
core 停了还有 uncore(L3、memory
controller、PCIe)。Intel “Uncore Frequency Scaling”、AMD 的
CCX 独立时钟。Linux 通过 intel_uncore_frequency
驱动暴露。
影响:
- 深 PC-state 把 uncore 关 → 内存访问第一 μs 慢
- I/O 热路径应避免深度
idle:
intel_idle.max_cstate=2、idle=halt
六、idle_inject 与 power capping
主动注入 idle 做温控:intel_powerclamp
把多个 CPU 按比例 idle 注入,达到 TDP / RAPL
限制。云服务商在密集服务器里用它防过热。
也有 cpuidle_haltpoll 与 KVM 的配合。
七、诊断
cpupower idle-info
# 列 governor、每 CPU 可用状态、统计
turbostat --quiet --interval 1
# 看 PkgWatt、CoreC6%、PkgC6%
perf stat -e power/energy-cores/ (RAPL
event)看 CPU 能耗。
bpftrace kprobe cpuidle_enter_state
看决策分布。
八、调优场景
8.1 低延迟网络服务
关深 C-state:
intel_idle.max_cstate=1 processor.max_cstate=1
# 或运行时
echo 1 > /sys/devices/system/cpu/cpu0/cpuidle/state3/disable
代价:空闲时功耗 +30-50%。
8.2 电池续航
默认 teo + P-state schedutil。Android 额外有 WALT / 自家 governor。
8.3 实时 (PREEMPT_RT)
限 C-state 最多到 C1;开 idle=poll 把空闲变
busy wait(最保延迟但爆功耗)。
8.4 虚拟化
guest:haltpoll governor;host:cpuidle +
kvm.halt_poll_ns 调 polling window。
九、idle 的 tickless 搭档
nohz_full=N:指定 CPU 上完全不收
timer tick。idle 不被唤醒;但需关闭很多 kernel
housekeeping。
配合 isolcpus + rcu_nocbs
用:专用 CPU 全时间用户态跑,连 1kHz tick 都没有。HPC /
网络转发面典型配置。
十、常见 bug
bug A:网络延迟 p99 飙几 ms 原因:CPU 进深 C-state,唤醒慢 解决:关深 state 或 cpu-dma-latency PM QoS
bug B:turbostat 显示 PC6 0%,功耗高
原因:某个 I/O / timer 阻止 uncore 进
PC;检查 /sys/kernel/debug/pm_genpd/ 或
turbostat 的 PkgWatt
bug C:guest 下延迟高
解决:换 haltpoll governor;调低
halt_poll_ns 或固定 CPU
bug D:AMD Ryzen idle 降频过激
解决:换
cpupower frequency-set -g performance 或
amd_pstate
十一、小结
- cpuidle 三层架构:驱动 + governor + 通用核
- menu(老默认)→ teo(现代)→ haltpoll(VM)
- C-state 每层都是”省电换退出延迟”
- 低延迟服务常禁深 state;电池场景相反
- nohz_full + idle=poll 是极端实时的搭档
下一篇 C-28(本子系列收尾)讲 cpufreq governor——频率调节,从 ondemand 到 schedutil 的演进。
参考文献
Documentation/admin-guide/pm/cpuidle.rst- Wysocki, R. “TEO cpuidle governor.” kernel commits / LWN 2018
- Linux source:
drivers/cpuidle/governors/*.c、drivers/idle/intel_idle.c - Intel “C-State Residency” 技术文档
- Brandenburg, B. “Idle states and real-time.” RTLWS 2019
工具
cpupower idle-info / idle-setturbostat—— 实时 C-state 比例powertop—— 图形化 idle / wake 源分析perf stat -e power/energy-*//sys/devices/system/cpu/cpuidle/*
上一篇:调度延迟分析 下一篇:cpufreq governor:频率调节
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。