
做内核功耗控制这些年我一直觉得 thermal framework 是最容易被忽视、却最不能出乱子的子系统。CPU 频率调慢一点顶多性能差些内存频率降一点也卡不死系统可温度一旦压不住轻则关机重启重则硬件老化甚至烧毁。尤其现在笔记本、服务器、嵌入式设备都在往高功耗密度走芯片厂商给的温控阈值越来越紧thermal 这一块已经从后勤保障变成了基础设施。这篇我尽量用做项目时的思路把 thermal framework 的通用架构从头到尾梳理一遍看完你至少能回答这几个问题thermal zone 和 cooling device 是怎么绑到一起的step_wise 和 power_allocator 到底在算什么为什么设备树里配了 trip point 系统却不动以及 thermal 和 cpufreq、devfreq 这些功耗子系统是怎么协作的。1. thermal framework 为什么要独立存在它不是 cpufreq 的附属品很多刚从驱动入门转内核的人会觉得温控嘛不就是温度高了把频率降下来直接挂在 cpufreq 里做不就行了早期有些 SoC 确实这么干过厂商在 cpufreq driver 里加一个温度回调超过阈值就限制 policy 的最大频率。这种方案的缺点很快就暴露了。1.1 热源不只有 CPU现代 SoC 里发热的大户至少包括 CPU、GPU、NPU、Modem、DDR 控制器、PMIC 供电路径、充电 IC。CPU 只是其中一路。如果只在 cpufreq 里做温度管理GPU 那边到 90 度根本没人管屏幕模组、摄像头 ISP 的发热更是失控。thermal framework 要解决的第一个问题就是把系统里所有能感知温度的点统一注册成 thermal zone把所有能降低功耗的器件统一建模成 cooling device然后通过统一的策略把温度映射到动作上。你可以把 thermal zone 想成一个个房间的温控器cooling device 想成空调、排风扇、窗帘这些执行机构。温控器只知道房间温度升到多少度了它不管空调是变频还是定频执行机构也不关心是哪个房间太热它只响应调节命令。这种解耦是 thermal framework 能通用的核心原因。1.2 降温手段远不止降频降 CPU 频率是降温手段但不是全部手段。以我调过的平台为例降温执行器包括CPU 调频cpufreq coolingCPU 调压通过 regulator 限制电压上限任务迁移/调度限制把任务从大核挪到小核GPU 降频devfreq coolingDDR 降频风扇转速提升屏幕亮度降低关闭充电或降低充电电流SoC 内部 AVSAdaptive Voltage Scaling调整这些执行器分布在不同的子系统和驱动里thermal framework 并不直接操作硬件它只定义一个标准的冷却设备接口——struct thermal_cooling_device_ops里面无非是get_max_state、get_cur_state、set_cur_state这几个回调。谁想成为一个 cooling device谁就注册一组 ops。这样cpufreq 是一个 cooling devicedevfreq 是另一个风扇驱动是第三个。框架层面完全不关心你降的是什么只关心当前冷却等级是多少和把冷却等级设到多少。1.3 框架层面解决的两个核心模型问题第一个模型问题温度变化是连续且滞后的。热容量大的器件你降频半小时温度才掉下来热容量小的几秒就冲上去。控制动作不能只看当前温度瞬时值要结合温度趋势。step_wisegovernor 就是靠温度在不在跳变区间来提前动作的而power_allocator更是用了 PID 的思想预测温度趋势来调整功耗预算。第二个模型问题多热源、多散热设备之间的耦合。一个大核和一个 GPU 挨得很近GPU 跑起来会拉高 CPU 的温度传感器读数。你如果只为 CPU 单独配一个冷却设备那么 GPU 发热导致 CPU zone 触发时你会莫名其妙地限制 CPU 频率但真正该管的是 GPU。所以 thermal framework 引入了thermal_instance允许一个 cooling device 同时被多个 thermal zone 引用也允许一个 thermal zone 绑定多个 cooling device。这种多对多的映射关系才是现实中芯片热模型的真实形状。在这里我必须强调一个很多人忽略的点thermal framework 做的是策略协调不是热仿真。它不会去算芯片内部哪个热点温度多高它拿到的温度已经是硬件 sensor 的最终结果。framework 的职责是根据温度点触发策略再根据策略调节冷却设备。理解这个边界你后面看thermal_zone_device_register那一堆参数就不会迷。2. zone、trip、governor、cdev四个对象的连接关系拆解内核里的 thermal 代码虽然分散在drivers/thermal/下但顶层对象就那么几个。我把它们之间的引用关系画在脑子里是一张很清晰的图每个thermal_zone_device维护一个 trip 列表和一个 cdev 绑定列表每个绑定项是thermal_instance每个 instance 关联一个thermal_cooling_devicegovernor 挂在 zone 上根据 zone 的温度和 trip 状态决定 instance 的 target state。2.1 thermal zone 的构成struct thermal_zone_device里最重要的字段除了 ops 之外还有trips一个struct thermal_trip数组表示温度阈值。num_tripstrips 的数量。temperature当前从 sensor 读到的温度。passive_delay和polling_delay两种轮询间隔。governor当前挂载的 governor 实例。thermal_instances绑定列表。struct thermal_trip包含typeactive/passive/hot/critical、temperature、hysteresis、flags。内核新版本里还加了target字段可以在 trip 里直接指定冷却目标不过兼容性还是老一套为主。注意一个关键点zone 本身不主动发起轮询温度。它有两种工作模式一种是 sensor 驱动主动上报温度thermal_zone_device_update里的THERMAL_TRIP_*事件另一种是 framework 用thermal_zone_device_check启动一个轮询定时器。具体走哪种取决于底层 sensor 是中断型还是轮询型。我们 GPIO 型温度传感器一般走轮询I2C 的商用 sensor 很多支持中断能省电。2.2 trip point 不只是四档规范里把 trip 分成active、passive、hot、critical四种。active 通常对应可以主动打开风扇的阈值passive 对应不需要用户感知的被动降频hot 是接近极限要快速动作critical 就是强制关机。但实现上trip 的具体行为完全由 governor 决定而不是由框架决定。比如 step_wise 遇到 active trip 也可以降频只要你把冷却设备的绑定方式配置成主动绑到 active trip 上。所以不要死板地认为 active 只用于风扇。在嵌入式里我们常把第一级 active 设成 CPU 降频的触发点第二级 passive 设成 GPU 亮度联动第三级 hot 设成 CPU 硬限频。一切都是设备树或 platform data 决定的。2.3 cooling device 与冷却等级冷却设备的核心是max_state和cur_state。state 为 0 表示不冷却state 越大表示冷却能力越强比如频率越低、风扇越快。cpufreq cooling device 注册时会自动根据频率表算出可用档位比如支持 5 个频点max_state 就是 4。set_cur_state回调里会做一件重要的事把高频到低频对应的所有 state 都映射到实际频率限制。这里有个坑不同次数频点的冷却曲线不一定是线性的。你可能频率从 1.8GHz 降到 1.6GHz 温度掉得很快但继续降到 1.4GHz 效果就不明显了。step_wise 这种简单 governor 不管这些它只看 state 数值power_allocator 则会结合功耗模型来算所以它在移动端用的多而且要和 dt 里的dynamic-power-coefficient搭配。2.4 thermal instance绑定关系的载体每次 zone 和 cdev 绑定时都会创建一个thermal_instance。它记录了trip这个绑定关系在哪个 trip 下生效。upper/lowercdev 在这个 trip 下允许的 state 范围。targetgovernor 计算结果写入的 state。一个 cdev 可以出现在多个 instance 里对应不同的 trip也可以被多个 zone 使用。最终生效的 state 是取的多个 zone 计算结果中的最大值保守策略也就是谁要求最严就听谁的。比如你的风扇被 CPU zone 和 GPU zone 同时控制CPU 侧要求 state 2GPU 侧要求 state 4风扇最终走 state 4。2.5 governor 的挂载方式系统里的 governor 有很多种fair_share、step_wise、power_allocator、user_space。zone 的governor_name可以通过设备树属性thermal-governor指定或者通过 sysfs 的policy节点切换。如果指定的 governor 没注册内核会回退到一个默认 governor通常是step_wise。这里我强烈建议做产品时把 governor 的选择当成整体功耗策略的一部分不要无脑选 power_allocator也不要无脑 step_wise。后面专门讲它们区别。3. 从代码看注册流程zone 和 cdev 是怎么牵手的我直接把关键调用链捋一遍这部分是纯源码视角能帮你建立从驱动注册到系统动作的完整链路。3.1 thermal zone 的注册流程底层 sensor 驱动要做的事核心是填充struct thermal_zone_device_opsstatic struct thermal_zone_device_ops my_sensor_ops { .get_temp my_sensor_get_temp, .get_trip_temp my_sensor_get_trip_temp, .get_trip_type my_sensor_get_trip_type, .set_trips my_sensor_set_trips, };然后调用tzd thermal_zone_device_register( soc_thermal, // 名字会在 sysfs 里显示 trips_array, num_trips, devdata, my_sensor_ops, passive_delay_ms, polling_delay_ms, passive_polling_delay_ms, // 新版本参数 governor_name );注册后驱动还要手动做一次thermal_zone_device_update(tzd, THERMAL_EVENT_UNSPECIFIED)触发初始检查。框架内部会为这个 zone 创建 sysfs 目录/sys/class/thermal/thermal_zoneX里面会出现temp、type、trip_point_*_temp、policy等节点。有个细节get_trip_temp是回调但内核在thermal_zone_get_trip时优先用的是trips数组里的静态值如果你动态改变 trip需要调thermal_zone_update_trip。很多驱动在运行时想调整 trip 温度却只改了内部变量sysfs 读到的还是旧值那是因为没有调 update 接口。这是实战里经常踩的坑。3.2 cooling device 的注册流程以 cpufreq cooling 为例驱动里cdev cpufreq_cooling_register(policy);其中会创建一个struct thermal_cooling_deviceops 来自cpufreq_cooling_ops。如果你想让自己的模块变成冷却设备比如驱动一个风扇cdev thermal_cooling_device_register(fan0, fan_data, fan_cooling_ops);里面其实做的工作是分配thermal_cooling_device调dev_register创建 sysfs 设备节点然后调用__thermal_cooling_device_register再走thermal_zone_device_register的内部绑定逻辑。注意在老版本内核里cdev 注册完不会自动绑定 zone需要 zone 驱动显式调thermal_zone_bind_cooling_device。新版本里如果你用设备树中的cooling-mapsof 接口会自动完成绑定。手动绑定与自动绑定可以混用但有冲突风险我见过 both 方式导致的重复 instance后文会写排查方法。3.3 设备树绑定一图流无 mermaid我给你一个典型的设备树配置结构它描述了一个 SoC 内部热区soc_thermal: thermal-zones { cpu_thermal: cpu-thermal { polling-delay-passive 250; polling-delay 1000; thermal-sensors tsens0 0; trips { cpu_alert0: cpu-alert0 { temperature 65000; hysteresis 2000; type passive; }; cpu_crit: cpu-crit { temperature 95000; hysteresis 2000; type critical; }; }; cooling-maps { map0 { trip cpu_alert0; cooling-device cpu_cooling0 0 2; }; map1 { trip cpu_alert0; cooling-device gpu_cooling 0 1; }; map_cluster { trip cpu_alert0; cooling-device cpu_alert0; }; }; }; };cooling-device三元组表示为phandle, 最低state, 最高state。比如cpu_cooling0 0 2允许 state 0 到 2如果 governor 算出 target3会被夹到 2。这种方式可以灵活限制某个 trip 下冷却设备的参与程度。关键点thermal-sensors 引用的 sensor 节点必须在驱动里实现 OF thermal helper 接口devm_thermal_of_zone_register等。如果你的 sensor 驱动没有实现get_temp回调zone 启动后会一直是非法温度毫无动作。3.4 注册顺序与 deferred probe有一个常见的启动日志thermal thermal_zone0: failed to find thermal zone。这类问题多半是cooling device 注册晚了。设备树解析时zone 会尝试 bind 所有 cooling-maps 里的 phandle如果对应的 cooling device 还没注册of 绑定流程会返回 -EPROBE_DEFERzone 注册会推迟。但如果你在 driver 里用thermal_zone_bind_cooling_device手动绑定就要注意调用时机最好放在component_late_probe或late_initcall里否则同样会失败。我在调试时习惯在启动日志里搜thermal关键字看每个 zone 是否成功创建以及 cdev 是否成功注册。一个健康系统的日志大概是thermal_sys: registered thermal governor fair_share thermal_sys: registered thermal governor step_wise thermal_sys: registered thermal governor power_allocator thermal_sys: registered thermal governor user_space thermal_sys: registered thermal zone cpu-thermal如果少了某个 governor多半是内核 config 没开CONFIG_THERMAL_GOV_POWER_ALLOCATOR之类的选项。4. governor 到底在算什么step_wise、fair_share 与 power_allocator 的实际差异很多人把 governor 想得太玄。其实它们就干三件事读 zone 温度比较 trip 阈值然后决定绑定的 cdev 该设到哪个 state。差别在于怎么比较、怎么决定。4.1 step_wise查表式的趋势判断step_wise是默认 governor也是最直观的。它维护每个 trip 的状态THERMAL_TRIP_*是否被触发还会根据温度是上升还是下降给出THERMAL_TREND_RAISING、THERMAL_TREND_DROPPING或THERMAL_TREND_STABLE。核心算法在step_wise的throttle函数里。它有一个关键概念叫上一个 trip 的 target。算法会找到当前温度所在的区间设在区间内所有已触发 trip 对应的 instance 上如果温度上升且越过 threshold在原有 state 基础上加 1。如果温度下降且低于 trip 的 hysteresis 回差在原有 state 基础上减 1。一次更新最多加 1 或减 1不会直接跳满。所以 step_wise 天然具备缓慢升档、缓慢降档的特性。加 1 减 1 的节奏由polling-delay决定。比如你 passive 轮询间隔 250ms每轮最多升一档那么从 state 0 升到 state 4 需要 1s。这是好事避免温度在阈值附近来回抖动导致频率疯狂波动。不过 step_wise 的缺点是它完全没考虑功耗模型。一个 cdev 从 state 0 到 1 可能降低了 500mW但从 state 3 到 4 可能只降低 50mW它不在乎。所以 CPU 的 cpufreq 曲线通常用 step_wise 问题不大但对功耗敏感的手机平台它不够精细。4.2 fair_share按权重摊派指标fair_share逻辑更简单每个 instance 有一个contribution权重通常在代码里写死或通过 thermal_instance 里的 weight 设定。它根据当前温度超过第一级 trip 的比例把总降温需求按权重分摊到各个 cdev。举个例子zone 里有 A、B 两个 cdev权重分别为 70 和 30。如果温度超过 trip 的比例是 40%那么 A 需要承担 28% 的冷却能力B 承担 12%。具体换算成 state 时按各自max_state乘一下比例取整。这个 governor 在 PC 主板行业还有一定使用率但在消费级 SoC 里我用得很少因为它对传感器滞后误差的容忍度低容易产生过冲。适合温度响应快、冷却设备线性度好的场景。4.3 power_allocator把温度问题翻译成功率预算问题power_allocator是这几年的重点。它的核心思路是既然温度是功耗积分得来的控制温度不如直接控制功耗。它先给 zone 一个可允许的最大功耗sustainable_power通常在设备树节点属性里写比如cpu_thermal 2500然后通过一个 PID 控制器算出当前功耗预算需要调整多少err temp - switch_on_temp; power_range sustainable_power Kp * err Ki * integral(err) Kd * derivative(err);然后对预算内的 cdev 按各自的power actorAPI 分配实际功率值。cpufreq 的 power actor 会根据动态功耗系数dynamic-power-coefficient单位 uA/MHz/V^2 等反推出允许的频率上限。使用 power_allocator 的前提是必须实现power2state或get_requested_power等 power actor 回调。设备树里要有dynamic-power-coefficient。zone 的sustainable_power要调得准否则 PID 会震荡。我自己的经验是power_allocator 不是拿来即用的。它需要花时间整定k_po、k_pu、k_i这些 PID 参数在 dt 的trips子节点里可配置。如果参数不对温度会像过山车。在快速交付的项目里我会先上 step_wise 跑通再去细调 power_allocator。4.4 选择 governor 的实战建议下表是我的经验总结仅供参考场景推荐 governor原因服务器 CPU 温控step_wise稳定、可预测、性能影响可控手机/平板 SoCpower_allocator需要精细功耗预算延长续航工业设备风扇控制fair_share多风扇均衡调试阶段或实验室user_space所有决策丢给用户态方便观测车载多热源耦合step_wise 自定义冷却映射避免牵一发动全身当然内核里还允许你写自己的 governor注册方式就是实现thermal_governor结构体然后thermal_governor_add。但多数项目改参数就够了没必要造新轮子。5. 设备树与驱动适配如何落地一个可靠的 thermal 方案前面说了理论这里写实际配置和排障。我在不同平台调过 thermal踩坑最深的往往不是 governor 算法而是设备树配置和驱动回调的细节。5.1 thermal zone 的 polling-delay 到底怎么设很多初学者困惑polling-delay-passive和polling-delay的区别。其实规则是polling-delayzone 未触发任何主动降温 trip 时的轮询间隔单位毫秒。这个值可以很大比如 1000 甚至 5000因为常温下不需要频繁读。polling-delay-passivezone 触发 passive 及以上 trip 后的轮询间隔。要小比如 100~500ms保证响应速度。如果你的 zone 只有一个恒温空调场景没有被动降温那polling-delay-passive不会被用到。但如果你的冷却机制是降频一定要设置polling-delay-passive否则温度越过阈值后还是用大间隔轮询反应迟钝可能导致瞬间冲高到 critical。我见过一个项目把polling-delay设 100mspolling-delay-passive没写结果默认是 0内核用默认值 1000ms 轮询。温度从 60 度跑到 90 度花了 3s系统没来得及降频就被 hot 保护了。这种问题不会报警只看温度曲线才发现。5.2 critical trip 不能只靠 frameworkcritical trip 指的是当温度达到阈值时framework 会调用orderly_poweroff尝试关机。但注意这个动作不是原子级别的如果温度和硬件安全上限只差一点建议更早设置 hot trip并让 hot trip 触发一个最激进的冷却动作比如瞬间把 CPU 限到最低频、关闭 GPU。critical 是最后一道闸不要指望它能优雅保存数据。另外critical trip 的温度设定要留足余量。考虑 sensor 精度 ±5 度器件温度与 sensor 读数的差异也可能有几度。比如芯片结温最大 105 度你设 critical 到 100 度其实很危险最好设 95 度hot 设 90 度。5.3 动态 trip 温度调整量产产品常碰到一个问题同型号不同批次的 sensor 校准偏差不同或者不同 SKU 的散热条件不同。这时不该改设备树而应该在 driver 里根据 eFuse 校准值动态调整 trip 温度。代码可以这样struct thermal_trip *trip thermal_zone_get_trip(tzd, trip_id); trip-temperature new_temp; thermal_zone_update_trip(tzd, trip, trip_id);thermal_zone_update_trip会重新计算 zone 状态并通知 governor。注意别在中断上下文里调用因为里面可能触发 governor 的 throttling需要可睡眠锁。另外sysfs 里trip_point_0_temp是可写的如果你的 ops 没有锁定写进去后内核会自动调set_trip_temp。很多自动化测试脚本就是靠这个做 thermal 测试的不用改固件就能模拟不同温度点。5.4 多 zone 多 cdev 互相争抢的问题当两个 zone 共用一个 cdev 时比如电池温度 zone 和 CPU zone 都管 cpufreq可能出现电池明明没发热只是 CPU 自己热结果 CPU 被压得很低的情况。解决办法是不同 zone 对 cdev 设不同的upper/lower范围并把governor的敏感度调低。或者用weight机制让某 zone 对 cdev 的影响权重变小。在内核新版本里thermal_instance有个weight字段配合 power_allocator 时会按权重来计算功率分配。如果你用的是 step_wiseweight 无效那就只能用上下限控制了。5.5 调试 thermal 的常见命令我调试时几乎离不开这几个节点cat /sys/class/thermal/thermal_zone*/temp cat /sys/class/thermal/thermal_zone*/type cat /sys/class/thermal/cooling_device*/cur_state cat /sys/class/thermal/cooling_device*/max_state cat /sys/class/thermal/thermal_zone*/policy还可以临时改 policyecho power_allocator /sys/class/thermal/thermal_zone0/policy想观察 governor 内部状态开启内核 tracetrace-cmd record -e thermal trace-cmd reportthermal的 tracepoint 会输出 zone 温度变化和 trip 触发事件是定位为什么没降频的第一手资料。6. thermal 与功耗子系统的协作从被动降温到主动功耗预算前面讲的大都是温度高了再去降频的闭环。但从功耗系统视角看thermal 还有更高级的玩法在温度还没升高之前就把功耗预算控制住。这就涉及到和 cpufreq、devfreq、PM QoS、energy model 的协作。6.1 cpufreq cooling 与 cpufreq governor 的互动cpufreq cooling device 的set_cur_state本质上是在调用cpufreq_update_policy把 policy 的max_freq限制到某个档位。但它不会干预 cpufreq governor 的内部选频逻辑只是把可用的最高频点封顶。这保证了 thermal 的介入不会影响调度器的性能预期也不会破坏 schedutil 的频率爬升逻辑。这类限制是有惯性的一旦max_freq被限制即使温度降下来step_wise 也要等温度低于 hysteresis 后才逐级恢复这是有意防止频率抖动。但极端场景下用户可能觉得性能迟迟不恢复这就要看你的降级步长和轮询间隔是否合理。6.2 devfreq coolingGPU/DDR 的温控路径GPU 和其他 devfreq 设备的冷却注册类似dcd devfreq_cooling_register(devfreq);它会根据 devfreq 的频率表建立冷却 state。因为 GPU 没有 schedutil 那么精细的调频逻辑冷却映射相对简单state 每加一档频率往下降一档。power_allocator对 devfreq 的功率预测通常依赖dynamic-power-coefficient这部分在新版内核里有现成的 power actor 实现老版本需要自己补。6.3 PM QoS 与 thermal 的关系PM QoS 分 CPU 和 device 两类限制。thermal 可以通过PM_QOS_CPU_DMA_LATENCY或PM_QOS_FREQ来约束硬件行为不过它一般不直接调 PM QoS而是通过 cooling device 的用户态接口或自定义驱动间接使用。如果你在写自定义冷却设备想要温度高时限制 DMA 延迟预算可以考虑在set_cur_state里更新 PM QoS 请求。6.4 用户态 thermal 和 thermal netlinkuser_spacegovernor 会把决策权交给用户态。老方式是 sysfs 里trip_point_0_type写成 user_space并监听uart或轮询 temp 节点。新内核提供thermal_netlink组播事件应用层可以通过 netlink 订阅温度变化、trip 触发、cdev 状态变化等事件。这个接口对实现智能温控策略非常有用比如手机上的游戏模式时用户态收到温度告警后直接调整 GPU 频率上限、屏幕刷新率、充电功率而不是让内核手动限频。我的建议是能放在内核里的决策不要上用户态。因为用户态进程可能被调度延迟关键 cooling action 一旦延迟几百毫秒温度可能已经越过临界。用户态更适合做宏观策略切模式、弹提醒、调充电微观 throttle 交给 kernel governor。6.5 energy model 与 thermal 的未来趋势近年内核引入了能源模型Energy Model框架drivers/thermal也开始和 EM 结合power_allocator可以通过 EM 更准确地估算每个 device 在不同频率下的功耗。未来的 thermal 会往预测式发展根据任务负载预测未来几秒的功耗和温度提前调整预算而不是等温度升上来再反应。这仍然是个活跃演进的方向但对普通产品研发来说理解当前的通用架构已经足够解决绝大多数工程问题。7. 一些实战排查经验按症状逐个说最后这一段我不写完整章节了直接把我这几年调过的典型问题清单列出来每条都是一次真实的排障记忆希望帮你少走弯路。问题 1温度明明到了 tripcdev 不动。先查两点一是cooling-maps里 trip 对应的 phandle 对不对二是 cdev 的upper/lower范围是不是把 target 卡死了。我有一次就是cooling-device cdev 0 0上限也是 0那 governor 怎么算都不可能让它动。问题 2zone 的 temperature 一直读 0。大概率是 sensor 驱动没实现get_temp或者 I2C 通信失败。还有可能是设备树thermal-sensors的 phandle 指向了父节点而不是具体的 sensor 子节点。用cat /sys/class/thermal/thermal_zone0/temp看一眼如果报错就查 dmesg。问题 3降温恢复太慢性能损失严重。把polling-delay-passive调大一点试试然后看 step_wise 的下降方向判断逻辑是否被 hysteresis 卡住。hysteresis 设太大会导致温度已经降到阈值以下很多才恢复设太小又会在阈值附近反复抖动。经验值传感器噪声大的设 2000~3000噪声小的设 1000。问题 4开机阶段 thermal 报错导致启动失败。常见是cooling-device的 phandle 指向一个还没 probe 成功的设备。检查日志里的deferred probe信息必要时把thermal模块的late_initcall优先级调低。不要在module_init里做绑定。问题 5critical trip 触发后系统反复重启。我建议在关机前加一个用户态 hook比如让thermal_zone的 hot trip 提前触发一个reboot命令而不是poweroff或者至少把关键日志持久化。否则 critical 关机后用户无法分析原因。内核提供了thermal_trip_notify回调可以在里面加打印或 panic dump。thermal framework 说到底是一个温度事件分发 冷却能力调度的框架。你不需要把每个 governor 的数学推到极限但只要能把握住 zone、trip、cdev、instance 这四个对象的协作关系再配合设备树和 sysfs 做验证绝大多数平台的温控问题都能在半小时内定位到根因。写这篇的目的就是帮你把这半小时缩短成五分钟。