资讯详情

Linux 6.6内核 CPU 深度解析(十五):CPU 状态全景 — hotplug / C-state / P-state 三个正交维度

📅 2026/10/25 1:53:54 | 华诺云谱 👁 阅读
Linux 6.6内核 CPU 深度解析(十五):CPU 状态全景 — hotplug / C-state / P-state 三个正交维度
〇、全景一台 CPU 的一生 三个正交状态机的叠加前面 #3hotplug 状态机、#5cpuidle、#6cpufreq分别讲了三个状态机但各自独立没讲它们怎么叠加成一台 CPU 的一生。本篇把它们串成一条时间线CPU 的一生是三个正交维度同时变化的过程——每个维度内部又有一大堆具体状态状态之间有交互和抢占某个状态缺失还会有连锁影响。维度状态机回答的问题具体状态取值存不存在hotplugcpuhp_state这台 CPU 能不能用OFFLINE → 几十个中间态 → ONLINE睡多深cpuidleC-state空闲时省多少电C0 / C1 / C1E / C3 / C6 / C7 / …跑多快cpufreqP-state干活时出多大力P0 / P1 / … / Pn turbo三者正交一台 CPU 可以同时是online C6 深睡 P0 满频准备随时满血干活、“online C0 运行 P1 降频”轻负载省电、或者offline直接不存在了。正交意味着互不冲突但有时序依赖和抢占——hotplug 是另两个的前提CPU 下线了C-state/P-state 都没意义中断和 hotplug 又会抢占C-state。生命周期有活来了没活了cpu_up 再来一次① 上线hotplug: OFFLINE→ONLINE② 干活C0 P-state 调频③ 空闲C0→C1→C6 下沉④ 下线hotplug: ONLINE→OFFLINEhotplug 维度存不存在P-state 维度跑多快只在 C0C-state 维度睡多深一句话主线CPU 的一生是三个正交状态机在同一颗核上的叠加——hotplug 决定存不存在几十个中间态C-state 决定空闲时睡多深C0/C1/C1E/C3/C6/C7 逐级加深P-state 决定干活时跑多快P0/Pnboost。生命周期是上线 → 干活 → 空闲 → 下线的循环状态之间会交互和抢占中断唤醒抢占 C-state、hotplug 抢占前两者、C1E 自带降频某个状态缺失也有连锁影响缺 C6 省电差、缺 hotplug 回调则子系统没初始化。三者最终在idle 循环do_idle里交汇。一、三个维度的具体状态清单1.1 hotplug不是只有 OFFLINE/ONLINE中间有几十个状态enum cpuhp_stateinclude/linux/cpuhotplug.h:57把拉起一台 CPU拆成一条几十个状态的有序回调链状态值是声明顺序不是行号。除了首尾OFFLINE0、ONLINE中间每个状态对应一个子系统的 per-CPU 初始化/清理挑几个关键的状态段对应子系统回调CPUHP_OFFLINE 0:61起点不在线CPUHP_WORKQUEUE_PREP:104PREPARE上线时 workqueue 准备 per-CPU workerCPUHP_RCUTREE_PREP:113PREPARERCU 每核状态准备CPUHP_TIMERS_PREPARE:131PREPARE高精度定时器准备CPUHP_BRINGUP_CPU:136PREPARE→STARTING 分界控制 CPU 代劳 → 目标 CPU 自己跑CPUHP_AP_IDLE_DEAD:142STARTING 起点目标 CPU 关中断跑CPUHP_AP_SCHED_STARTING:145STARTING调度器 rq 就位CPUHP_CPUIDLE_DEAD:81PREPARE下线下线时 cpuidle 的 teardownCPUHP_TEARDOWN_CPU:199下线分界AP 线程逆序拆自己CPUHP_AP_ONLINE_IDLE:202ONLINE 起点能跑 hotplug 线程了CPUHP_AP_ACTIVE:257ONLINE已激活、可被调度CPUHP_ONLINE:258终点完全在线关键认知hotplug 状态机的价值不在首尾在中间——CPUHP_WORKQUEUE_PREP、CPUHP_AP_SCHED_STARTING、CPUHP_CPUIDLE_DEAD这些状态每个都挂着某个子系统的 startup/teardown 回调。上线时从 OFFLINE 顺序跑一遍 startupworkqueue→RCU→timer→sched→…→ONLINE下线时逆序跑 teardown…→cpuidle→…→OFFLINE。这就是 #3 讲的有序回调链。1.2 C-state具体有 C1/C1E/C3/C6/C7…每级有代价C-state 不是睡/醒两档而是一串由浅到深的档位每档由struct cpuidle_stateinclude/linux/cpuidle.h:49刻画两个参数是选状态的依据// include/linux/cpuidle.h (v6.6, line 49)structcpuidle_state{charname[CPUIDLE_NAME_LEN];s64 exit_latency_ns;// :53 醒过来要多久性能账s64 target_residency_ns;// :54 停多久才回本能量账...int(*enter)(structcpuidle_device*,structcpuidle_driver*,int);// :60 进入状态的底层动作};intel_idledrivers/idle/intel_idle.c按 CPU 代际维护真实的状态表例如 Nehalemnehalem_cstates:234状态MWAIT hintexit_latency (µs)target_residency (µs)特性C10x0036最浅只停流水线C1E0x011020停转 降频C30x102080刷 TLBCPUIDLE_FLAG_TLB_FLUSHEDC60x20200800关核心电源、刷 cacheSandy Bridgesnb_cstates:271多了C7MWAIT 0x30exit_latency109、target_residency345Bay Trailbyt_cstates:316甚至有 C6N/C6S/C7 等变体exit_latency 最高 1200µs。规律很清晰状态越深exit_latency越大醒得越慢、target_residency越大要停更久才回本——这就是 menu governor 选状态的两个依据。1.3 P-stateP0/Pn 频率档位 turbo boostP-state 是频率电压的组合档位P0最高频、最高压→Pn逐级降低。现代 x86 还有一个turbo boost睿频短暂地把单核频率超到标称 P0 之上用别核空闲的热预算由硬件自主管理X86_FEATURE_HWP见 #13。schedutil默认 governor在 P0~Pn 之间按 util 实时调频触发点是调度器的update_util 回调每次调度 tick、任务切换时// kernel/sched/cpufreq_schedutil.c (v6.6, line 139)staticunsignedintget_next_freq(structsugov_policy*sg_policy,...){...freqmap_util_freq(util,freq,max);// :147 频率 max × util / max_cap}P-state 只在 C0运行态有意义CPU 都睡了就没有跑多快的问题详见 §3.4。turbo 则是硬件层面的额外加成。二、完整生命周期时间线什么时候跳什么状态阶段 ① 上线hotplug OFFLINE → ONLINEBSP加电即在线boot_cpu_initkernel/cpu.c:3155直接四态置位、boot_cpu_hotplug_init直接写CPUHP_ONLINE不走完整cpu_up因为它本来就已经上线。APINIT-SIPI 唤醒#2→ trampoline →secondary_startup_64→start_secondary→ 状态机从CPUHP_OFFLINE一路 startupworkqueue→RCU→timer→sched→…到CPUHP_ONLINE。关键分界CPUHP_BRINGUP_CPU之前控制 CPU 代劳准备之后目标 CPU 能跑线程了自己做。上线完成的标志状态走到CPUHP_ONLINECPU 进入cpu_online_mask可被调度。阶段 ② 干活C0 P-state 调频上线后 idle 线程进cpu_startup_entry→do_idle。有任务时need_resched置位 → 跳出 idle →schedule_idle()idle.c:310切到任务。此刻 CPU 在C0schedutil 按 util 调 P-state负载高 → 升频逼近 P0甚至 turbo负载低 → 降频往 Pn。干活这一秒hotplug 恒 ONLINE、C-state 恒 C0只有 P-state 在动。阶段 ③ 空闲C0 → C1 → C6 下沉没活干do_idle里while (!need_resched())idle.c:258成立进cpuidle_idle_call:282→ menu governor 预测 idle 时长 → 选一个 C-state →cpuidle_enter_state执行enterintel_idle 用mwait指定 hint。C-state 维度在动C0→C1→C1E→C3→C6 逐级下沉P-state 冻结睡了没频率hotplug 恒 ONLINE。阶段 ④ 下线hotplug ONLINE → OFFLINEcpu_down→ 状态机逆序 teardown…→cpuidle→workqueue→…→takedown_cpu→stop_machine_cpuslocked(take_cpu_down)cpu.c:1345关中断空转做下线收尾并发 #3 死锁篇讲了这个过程的死锁风险。最后 idle 线程回do_idle发现cpu_is_offline(cpu)idle.c:263// kernel/sched/idle.c (v6.6, line 263)if(cpu_is_offline(cpu)){tick_nohz_idle_stop_tick();cpuhp_report_idle_dead();// :265 报告我死了arch_cpu_idle_dead();// :266 hlt 死等永不再醒}下线完成 hotplug 走到 OFFLINECPU 移出cpu_online_maskidle 线程 hlt 死等——C-state 和 P-state 随之失去意义。三、状态的交互与抢占谁抢谁三个状态机不是各玩各的它们之间有明确的抢占关系3.1 中断 / 任务唤醒抢占 C-state最频繁CPU 在 C6 深睡时硬件收到中断网卡包、定时器、IPI或 wakeup 事件 →硬件立刻把核心拉回 C0处理。这是最频繁的抢占——每秒成千上万次。代价就是exit_latencyC6 要 200µs 才能醒所以 menu governor 宁可预测 idle 太短时选浅 C-state也不让深睡→立刻被唤醒白白付 exit_latency 的账。3.2 hotplug 抢占 C-state P-state最高优先级cpu_down是最高优先级的抢占不管 CPU 此刻在 C0 跑任务、还是 C6 深睡都先唤醒如果在睡然后逆序 teardown 掉 cpuidleCPUHP_CPUIDLE_DEAD、cpufreq、调度器等最后 idle 线程arch_cpu_idle_dead死等。hotplug 一下线C-state 和 P-state 直接失去存在基础——这是三者之间唯一的硬性依赖。3.3 C-state 内部menu governor 预测决定浅睡 vs 深睡同一颗空闲的 CPU选 C1 还是 C6是 menu governor#5在抢它用两个预测器next_timer × 校正因子 重复间隔检测估出 idle 时长再和每个状态的target_residency/exit_latency比——预测短就选浅睡C1预测长才敢选深睡C6。选深了醒太慢below选浅了浪费省电机会above这个反馈闭环就是预测对状态选择的持续抢占。3.4 C-state 与 P-stateC0 才有 P-stateC1E 自带降频严格说 C-state 和 P-state 是互斥的P-state 只在C0运行态有意义进 C1 以上核心睡了频率设置失效尤其 C6 关核心电源P-state 寄存器直接断电。唯一的例外是C1E——它是停转 降频的复合态等于 C-state 里内置了一次 P-state 的降低。所以省电这条主线上C-state 和 P-state 是接力关系C0 时靠 P-state 降频省电进 C1 后靠 C-state 关部件省电。四、状态缺失的影响缺一个状态会怎样4.1 hotplug 状态缺失子系统 per-CPU 数据没初始化某个子系统的 CONFIG 没开比如没编CONFIG_SMP相关的某个功能它就不会往cpuhp_hp_states[]注册回调对应状态是空的。后果上线时那个子系统的 per-CPU 数据没初始化访问到空指针/未初始化内存下线时没清理泄漏 per-CPU 资源。这是 #10 初始化全景讲的每核那份 per-CPU 数据在缺失时的直接后果。4.2 C-state 深度缺失省电能力被砍硬件只支持到 C1没有 C6或intel_idle没加载退化成只有hlt C1idle 时最深只能停流水线关不了核心电源。功能完全正常但待机功耗高——C1 只省空转的电C6 才省核心电源的电移动设备/数据中心待机的差距巨大。反过来若 CPU 支持深睡但target_residency设太大menu governor 永远不敢选深睡效果等同缺失。4.3 P-state 缺失跑在固定频率cpufreq driver 没加载acpi_cpufreq/intel_pstate没起来或X86_FEATURE_HWP没开CPU 跑在BIOS 设定的固定频率。后果负载低时不能降频省电负载高时若非 turbo不能升频功耗和性能两头都不讨好。现代服务器基本都靠intel_pstate的 HWP 让硬件自主调频缺失等于退回固定频率的原始状态。五、三者的交汇点do_idle是枢纽三个状态机在同一段代码里交汇枢纽就是idle 循环do_idlekernel/sched/idle.c:237C-state 的载体cpuidle_idle_call():282在这里调用idle 循环就是 C-state 的驾驶舱——反复选状态、进状态、被唤醒、再选。hotplug 下线的归宿cpu_is_offline(cpu):263cpuhp_report_idle_dead:265arch_cpu_idle_dead:266在这里执行下线后的 CPU 走到报告 dead hlt 死等的终点。P-state 的边界while (!need_resched()):258是 C-state睡和 P-state跑的分水岭——need_resched一置位就跳出 idle 去跑任务进入 C0 P-state 调频否则钻进 C-state 睡觉。一句话do_idle同时是空闲时睡多深C-state、“下线后去哪”hotplug 终点、“什么时候回到干活”P-state 起点的交汇点。六、为什么这样设计Why 层6.1 为什么存在 / 深浅 / 快慢要拆成三个正交状态机因为它们是三个独立的问题、三个独立的优化目标hotplug 管节能之外还要不要这台 CPU调度、隔离、故障下线C-state 管空闲时怎么省电性能 vs 能量的两本账P-state 管干活时怎么省电利用率 vs 频率。耦合在一个状态机里会导致想下线和想降频互相牵制、状态爆炸。正交拆解让每个维度各自用最合适的机制回调链 / 预测 governor / 利用率调频独立演进。6.2 为什么 C-state 要分这么多档而不是睡/醒两档因为省电深度和唤醒延迟不可兼得睡得越深关核心电源越省电但醒来越慢exit_latency 越大。如果只有睡/醒两档睡必须选最深短空闲也付 200µs 的醒来代价得不偿失。多档让 governor 按预计空闲多久选最划算的那一档——短空闲选浅睡、长空闲选深睡省电和延迟两头兼顾。6.3 为什么 hotplug 是另两个的前提且能抢占它们C-state 和 P-state 描述的都是一台在线CPU 的行为。CPU 一下线idle 线程在do_idle里直接arch_cpu_idle_dead死等C-state/P-state 的机制根本不会再被走到。所以 hotplug 是底层“下线是比进 C-state”调 P-state都更高的优先级——这是三者之间唯一的硬性依赖其余维度彼此正交。6.4 为什么交汇点选在 idle 循环而不是别处idle 循环是 CPU唯一既不干活、又没下线的稳定状态——它是有任务和没任务之间的边界天然适合做两个方向的裁判没任务 → 沉入 C-state检测到 offline → 沉入死亡。把三个维度的切换都收进do_idle一个循环就避免了三套状态机各自维护一套CPU 空闲时我该干嘛的逻辑。七、与已有博客的交叉引用已有博客本篇覆盖的关系CPU #3 hotplug 状态机§1.1 的关键状态WORKQUEUE_PREP/AP_SCHED_STARTING/CPUIDLE_DEAD是 #3 状态机链上的具体回调本篇只取关键状态讲生命周期CPU #5 cpuidle§1.2 的 C-state 深度表intel_idle 的 exit_latency/target_residency是 #5 menu governor 选状态的输入CPU #6 cpufreq§1.3 的 P0/Pnboost、§3.4 的C0 才有 P-state是 #6 schedutil 调频的状态边界CPU #2 AP 拉起start_secondary→cpu_startup_entry→do_idle是上线 → idle衔接的链路来源CPU #10 初始化全景#10 串初始化流程哪些数据做几次本篇串运行期状态什么时候跳什么状态 状态缺失的连锁§4.1 呼应 #10 的 per-CPU 数据并发 #3 死锁下线阶段阶段④的stop_machinedo_idle检测 offline正是死锁风险的藏身处附本篇关键源码索引符号位置enum cpuhp_statehotplug 状态机include/linux/cpuhotplug.h:57CPUHP_OFFLINE/CPUHP_BRINGUP_CPU/CPUHP_ONLINEinclude/linux/cpuhotplug.h:61/:136/:258CPUHP_WORKQUEUE_PREP/CPUHP_AP_SCHED_STARTING/CPUHP_CPUIDLE_DEADinclude/linux/cpuhotplug.h:104/:145/:81CPUHP_TEARDOWN_CPU/CPUHP_AP_ONLINE_IDLE/CPUHP_AP_ACTIVEinclude/linux/cpuhotplug.h:199/:202/:257struct cpuidle_stateC-stateinclude/linux/cpuidle.h:49nehalem_cstatesC1/C1E/C3/C6 深度表drivers/idle/intel_idle.c:234snb_cstates含 C7/byt_cstates含 C6N/C6Sdrivers/idle/intel_idle.c:271/:316flg2MWAITmwait hint 编码drivers/idle/intel_idle.c:130do_idleidle 循环三者交汇点kernel/sched/idle.c:237cpuidle_idle_callC-state 入口kernel/sched/idle.c:282cpu_is_offline→cpuhp_report_idle_dead→arch_cpu_idle_dead下线归宿kernel/sched/idle.c:263/:265/:266schedule_idle从 idle 切回任务kernel/sched/idle.c:310get_next_freqschedutil 调频kernel/sched/cpufreq_schedutil.c:139takedown_cpu→stop_machine_cpuslocked下线收尾kernel/cpu.c:1328/:1345boot_cpu_initBSP 直接置在线kernel/cpu.c:3155
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑