1. 项目概述为什么你得花时间搞懂 thermal framework 的通用架构在嵌入式设备、笔记本、服务器甚至手机 SoC 的 Linux 内核开发中thermal framework 不是“可选项”而是“生死线”。我做过三款 ARM64 工业边缘网关的内核定制其中两款在高温车间实测时连续重启——不是因为驱动崩溃也不是因为内存泄漏而是 thermal governor 在 78℃ 触发了紧急关机。当时 debug 日志里只有一行thermal thermal_zone0: critical temperature reached(95 C), shutting down。没人想到问题根源不在散热片厚度而在于 thermal zone 的 trip point 配置顺序写反了导致 passive cooling 没来得及介入系统就跳过了 throttling 直接 shutdown。这让我彻底意识到thermal framework 不是“温度监控模块”它是一套带策略调度、状态机驱动、跨子系统协同的功耗治理中枢。它和 cpufreq、devfreq、powercap、idle state 紧密咬合共同构成内核功耗子系统的“神经反射弧”。你调一个 CPU 频率thermal 可能立刻干预你启一个 GPU 渲染任务thermal 可能悄悄降频显存控制器你插上 USB-C 外接屏thermal zone 甚至会动态加载新的 cooling device。这种耦合不是松散的事件通知而是通过 struct thermal_zone_device 和 struct thermal_cooling_device 的双向引用、workqueue 延迟执行、notifier chain 实时广播构建的强一致性模型。所以本文不讲“怎么读 /sys/class/thermal/ 下的温度值”也不堆砌thermal_zone_device_register()函数原型。我们要拆的是它的骨架zone 怎么注册、trip 怎么分级、cooling 怎么绑定、governor 怎么决策、state 怎么迁移。这些设计背后藏着 Linus 早年在邮件列表里反复强调的一句话“Thermal management must bepredictable, not just reactive.” —— 可预测性才是 thermal framework 架构设计的第一性原理。如果你正在做国产化 ARM 平台适配、车规级 SoC 驱动移植、或准备 Linux 内核面试中“功耗管理”这一题那么这套通用架构就是你绕不开的底层契约。2. thermal framework 整体设计与思路拆解从“温度传感器”到“功耗策略引擎”的跃迁thermal framework 的设计哲学本质上是一次对传统“温度告警”思维的彻底颠覆。早期嵌入式系统常用 GPIO 中断接热敏电阻温度超限就拉低 RESET 引脚——这是纯硬件的“熔断机制”。Linux 内核没有照搬这条路而是把 thermal 抽象成一个可编程、可扩展、可策略化的软件子系统。它的核心设计目标有三个分层解耦、策略可插拔、状态可追溯。这三个目标直接决定了整个架构的四层结构Device Driver 层 → Thermal Core 层 → Governor 层 → Cooling Device 层。我们逐层看它为什么这么设计。首先Device Driver 层负责“感知真实世界”。但 thermal framework 不要求传感器驱动自己实现温度计算逻辑。比如一个 I2C 接口的 MAX6642 温度芯片驱动只需实现read_temp()回调返回原始 ADC 值而真正的摄氏度换算、校准偏移、滤波算法全部由 thermal core 统一接管。这样做的好处是同一套 thermal core 代码既能跑在 x86 服务器读取 ACPI _TMP 方法也能跑在 ARM64 芯片读取 SoC 内部 thermal sensor 寄存器还能跑在 RISC-V 开发板读取 GPIO 模拟电压。驱动开发者不用关心“温度单位”内核维护者不用为每种传感器写重复的滤波代码。这种解耦让 thermal framework 具备了极强的硬件无关性。其次Thermal Core 层是整个系统的“中央调度室”。它定义了struct thermal_zone_device这个核心数据结构里面不仅存着当前温度、trip point 数组、cooling device 列表还藏着一个关键字段struct thermal_governor *governor。注意这里不是指针数组而是一个单指针。这意味着一个 thermal zone 在任意时刻只能运行一种 governor 策略。这个设计看似限制实则是为了保证决策唯一性。试想如果 passive governor 和 bang-bang governor 同时生效一个要逐步降频一个要全速开关CPU 频率就会在 100MHz 和 2GHz 之间疯狂抖动系统稳定性直接归零。所以 thermal core 强制单 governor所有策略冲突都在这一层被消解。第三Governor 层是“大脑”。目前内核主线支持四种 governorstep_wise阶梯式、bang_bang开关式、user_space用户态接管、power_allocator功率分配式。它们的区别不在算法复杂度而在响应粒度和控制自由度。step_wise每次只调整一个 step如降低一级频率适合需要平滑过渡的场景bang_bang只有 on/off 两种状态响应最快适合风扇控制power_allocator则引入了 PID 控制思想根据温度误差积分动态分配 cooling device 的 power budget这是为高性能计算平台准备的精密调控方案。选择哪个 governor不是看“谁更高级”而是看你的硬件响应特性风扇启动延迟大选bang_bangCPU 频率切换耗时长选step_wise需要精确控温到 ±0.5℃那power_allocator才是正解。最后Cooling Device 层是“手脚”。它把抽象的“降温指令”翻译成具体的硬件动作。struct thermal_cooling_device封装了set_cur_state()回调这个回调里可以写任何东西调用 cpufreq_set_policy() 改变 CPU 频率上限、向 PWM 控制器写占空比寄存器、给 GPIO 发高低电平控制风扇、甚至调用 devfreq_set_target() 降频 GPU。关键点在于cooling device 是完全独立注册的和 thermal zone 解耦。一个 cooling device 可以被多个 zone 共享比如同一个 CPU cooling device 同时被 cpu_thermal_zone 和 gpu_thermal_zone 绑定一个 zone 也可以绑定多个 cooling device比如同时绑定 cpu_cooling 和 fan_cooling。这种多对多关系靠thermal_zone_bind_cooling_device()函数里的struct thermal_bind_params结构体维护里面定义了 binding priority、weight、k_po、k_pu 等参数——这些参数决定了当多个 cooling device 同时被请求时谁先动作、谁动作幅度大。这就是 thermal framework 的“策略可插拔”本质你换一个 governor整个 zone 的行为逻辑就变了你加一个 cooling devicezone 的调控手段就多了你改一个 trip pointzone 的响应阈值就移了。所有变化都不需要动 core 层代码只要遵循接口契约即可。提示很多初学者误以为 thermal framework 的核心是“读温度”其实恰恰相反。它的核心是“做决策”。温度读取只是触发条件真正有价值的是当温度从 70℃ 升到 75℃ 时系统该选择降频 CPU 还是提速风扇该降一级还是降两级该立即执行还是延迟 2 秒再执行这些决策逻辑全部封装在 governor 和 binding 参数里。理解这一点才能跳出“查温度文件”的初级阶段。3. 核心细节解析与实操要点zone 注册、trip 分级、binding 权重与 governor 切换要真正掌控 thermal framework光知道分层不够必须抠进每个关键结构体的字段含义、函数调用的时机约束、以及配置参数背后的物理意义。下面我结合实际调试案例把最常踩坑的四个核心环节掰开揉碎讲清楚。3.1 thermal zone 注册不是“注册完就完事”而是“状态机初始化”注册一个 thermal zone标准流程是调用thermal_zone_device_register()。但很多人忽略了一个致命细节这个函数的第 5 个参数const struct thermal_zone_device_ops *ops里面get_temp回调的执行上下文决定了你能不能在里面 sleep。官方文档明确写着“get_temp()must not sleep.” 为什么因为 thermal core 在周期性轮询默认 2 秒一次或中断触发时会直接在 softirq 上下文中调用它。如果你在get_temp()里用了msleep(1)或mutex_lock()整个系统 softirq 就卡死了。我曾经在一个 Allwinner H6 平台上遇到过驱动里为了等 ADC 转换完成写了wait_event_timeout()结果系统每隔 2 秒就卡顿 50ms网络 ping 延迟飙升到 300ms。解决方法只有两个要么改用 polling 方式循环读寄存器直到 ready bit 置位要么把 ADC 读取放到 workqueue 里异步执行get_temp()只返回上次缓存的值。后者更安全但需要你自己维护一个struct delayed_work和保护缓存的 spinlock。另一个易错点是tzd-passive_delay和tzd-polling_delay字段。前者是 passive cooling如降频触发后的轮询间隔后者是未触发任何 trip 时的常规轮询间隔。注意passive_delay必须小于polling_delay否则 passive cooling 一旦启动系统反而会降低轮询频率导致温度失控。实测中polling_delay20002 秒passive_delay5000.5 秒是工业设备的黄金组合——既保证常规监控不耗电又确保降频后能快速响应温度变化。3.2 trip point 分级critical 不是“最高级”而是“不可逆临界点”trip point 是 thermal zone 的“决策触发器”但它绝不是简单的“温度阈值数组”。内核定义了五种 typeTHERMAL_TRIP_ACTIVE、THERMAL_TRIP_PASSIVE、THERMAL_TRIP_HOT、THERMAL_TRIP_CRITICAL、THERMAL_TRIP_INVALID。其中CRITICAL最具迷惑性。很多人以为它是“最高温度档”其实不然。CRITICAL的语义是“此温度下硬件已处于不可逆损伤风险必须立即执行硬性保护且禁止任何用户空间干预”。这意味着一旦CRITICALtrip 被触发thermal core 会绕过所有 governor直接调用thermal_zone_device_disable()然后执行emergency_shutdown()。此时/sys/class/thermal/thermal_zone0/mode会被强制设为disabled连 root 用户都改不回来。所以CRITICAL的温度值必须严格等于芯片 datasheet 里标注的 “Maximum Junction Temperature”比如 Rockchip RK3399 是 125℃不能写成 120℃太保守浪费性能也不能写成 130℃太激进烧芯片。而PASSIVE才是真正的“最高调控档”它触发后会启动 governor 的主动降温策略此时用户仍可通过 sysfs 修改cur_state或mode。trip point 的排序也至关重要。内核要求所有 trip point 必须按温度升序排列且CRITICAL必须是最后一个。如果你把CRITICAL放在第 0 位PASSIVE放在第 1 位当温度升到 95℃CRITICAL阈值时thermal core 会先遍历 trip[0]发现匹配立刻 shutdown根本不会检查后面的PASSIVE。这个 bug 我在飞腾 D2000 平台移植时遇到过原因是 dts 文件里 trip 节点顺序写反了调试花了整整两天。3.3 cooling device bindingweight 不是“权重”而是“调控优先级系数”把 cooling device 绑定到 thermal zone用的是thermal_zone_bind_cooling_device()。它的第 4 个参数unsigned long weight文档里叫“weight”但实际作用远不止“权重”。这个值参与一个关键计算cdev-state * weight / total_weight。其中cdev-state是当前 cooling device 的状态如 CPU frequency level 0~10total_weight是该 zone 下所有绑定 cdev 的 weight 总和。最终结果决定了这个 cdev 在 governor 决策中的“贡献比例”。举个例子zone 绑定了 cpu_coolingweight80和 fan_coolingweight20当前 governor 计算出需要 60% 的 cooling capacity。那么 cpu_cooling 会执行state 610 级中的第 6 级fan_cooling 会执行state 15 级中的第 1 级。可见weight 直接决定了不同 cooling device 的“调控话语权”。更隐蔽的坑在k_po和k_pu参数。它们是struct thermal_bind_params里的浮点系数用于 passive governor 的 PID 计算。k_po是 proportional gain for passive coolingk_pu是 for active cooling。默认值都是 1.0但实测中对于响应慢的风扇启动延迟 500msk_po设为 0.3 更稳对于响应快的 CPU 频率调节切换 10msk_pu设为 2.0 更灵敏。这些参数没有标准值必须根据硬件实测调整。我的做法是先用示波器抓 PWM 波形测出风扇从 off 到 full speed 的上升时间再用echo 1 /sys/class/thermal/thermal_zone0/emul_temp模拟升温观察温度曲线斜率反推最优 k 值。3.4 governor 切换不是“echo 就生效”而是“状态机重置”切换 governor看似简单echo step_wise /sys/class/thermal/thermal_zone0/governor。但背后发生的事远比 echo 复杂。thermal core 会先调用旧 governor 的.exit()回调如果有然后调用新 governor 的.init()回调最后清空该 zone 的所有 cooling device 的 current state 缓存。这意味着切换 governor 的瞬间所有 cooling device 会回到初始 state通常是 0。如果你在step_wise下 CPU 正在 1.2GHz 运行切到bang_bang的瞬间CPU 频率会立刻跳回最低档如 400MHz造成业务中断。这不是 bug而是设计使然——因为不同 governor 对 state 的语义定义不同step_wise的 state5 表示“降 5 级”bang_bang的 state1 表示“全速运行”两者无法直接映射。因此生产环境严禁 runtime 切换 governor。正确做法是在 dts 或 kernel cmdline 里静态指定thermal-gov step_wise;。如果真需要动态切换比如调试必须配合 cooling device 的 state 锁定。方法是先echo 0 /sys/class/thermal/thermal_zone0/policy禁用自动调控再echo new_gov /sys/class/thermal/thermal_zone0/governor最后echo 1 /sys/class/thermal/thermal_zone0/policy。这样能保证切换过程中 cooling device state 不被重置。注意/sys/class/thermal/thermal_zone0/mode文件有两个值enabled和disabled。很多人以为disabled是关闭 thermal 功能其实它只是禁用自动调控所有 trip point 依然有效CRITICAL触发时照样 shutdown。真正关闭 thermal zone必须调用thermal_zone_device_unregister()这通常只在驱动卸载时做。4. 实操过程与核心环节实现从 DTS 描述到内核启动的完整链路现在我们把前面所有理论串成一条可落地的实操链路。以一款基于 NXP i.MX8MQ 的工业网关为例它的 SoC 内置 thermal sensor需要绑定 CPU cooling device并启用 passive cooling。整个过程分为四步DTS 描述 → Kernel 配置 → Driver 实现 → 启动验证。每一步我都给出可直接复制的代码片段和关键注释。4.1 DTS 描述用 device tree 定义硬件拓扑关系DTS 是 thermal framework 的“宪法”它定义了硬件资源如何被内核识别和组织。i.MX8MQ 的 thermal node 必须放在soc0节点下并显式引用 cooling devicesoc { thermal-zones { cpu_thermal: cpu-thermal { /* thermal zone 名称将出现在 /sys/class/thermal/ 下 */ compatible thermal-zones; /* trip point 定义按温度升序排列 */ trips { trip0 { temperature 60000; /* 60℃ */ hysteresis 2000; /* 滞后 2℃防止抖动 */ type active; }; trip1 { temperature 75000; /* 75℃ */ hysteresis 2000; type passive; }; trip2 { temperature 95000; /* 95℃ */ hysteresis 0; type critical; }; }; /* cooling device 绑定指定绑定对象和参数 */ cooling-maps { map0 { /* cooling device 名称必须和 cpufreq driver 注册的一致 */ cooling-device cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT; /* weight100表示 CPU cooling 是主要调控手段 */ weight 100; /* k_po1.0用于 passive governor 的 PID 计算 */ k_po 1000; /* 内核内部乘以 1000 存储 */ }; }; }; }; };关键点解析cooling-device cpu0 ...中的cpu0必须和 cpufreq driver 的thermal_cooling_device_register(cpu0)名称完全一致大小写敏感THERMAL_NO_LIMIT表示不限制 cooling device 的 min/max state实际范围由 cpufreq policy 决定k_po 1000是整数存储内核会除以 1000 得到 1.0。4.2 Kernel 配置最小化裁剪避免隐式依赖thermal framework 依赖多个子系统配置不当会导致编译失败或运行时 panic。以下是必须开启的 config 项基于 5.10 LTSCONFIG_THERMALy CONFIG_THERMAL_OFy # 必须否则 DTS 描述无效 CONFIG_THERMAL_GOV_STEP_WISEy # 选择你要的 governor CONFIG_THERMAL_GOV_BANG_BANGy CONFIG_THERMAL_GOV_USER_SPACEy CONFIG_THERMAL_EMULATIONy # 调试必备允许 echo 温度模拟 CONFIG_CPU_THERMALy # CPU cooling device 支持 CONFIG_DEVFREQ_THERMALy # 如果要用 GPU/DDR cooling CONFIG_ARM_SCPI_PROTOCOLy # i.MX8MQ 需要 SCPI 协议读取 sensor特别注意CONFIG_THERMAL_OF如果关闭内核会忽略 DTS 里的 thermal-zones 节点即使你写了 trip pointthermal_zone_device_register()也不会被调用。这个 config 在 menuconfig 里藏得很深路径是Device Drivers - Thermal drivers - * Generic thermal sysfs driver下面有个子选项[*] OF support。4.3 Driver 实现sensor driver 与 cooling device driver 的协同thermal sensor driver 的核心是实现struct thermal_zone_of_sensor_ops。以 i.MX8MQ 为例它的 sensor 通过 SCPI 协议通信driver 代码骨架如下static int imx8mq_thermal_get_temp(void *data, int *temp) { struct imx8mq_thermal *tz data; u32 val; int ret; /* SCPI 协议读取非阻塞 */ ret scpi_get_sensor_value(tz-scpi_id, val); if (ret) return ret; /* 硬件返回的是 millidegree转换为 microdegree */ *temp val * 1000; // 内核 thermal core 要求单位是 millidegree return 0; } static const struct thermal_zone_of_sensor_ops imx8mq_sensor_ops { .get_temp imx8mq_thermal_get_temp, }; static int imx8mq_thermal_probe(struct platform_device *pdev) { struct imx8mq_thermal *tz; struct device_node *np pdev-dev.of_node; int ret; tz devm_kzalloc(pdev-dev, sizeof(*tz), GFP_KERNEL); if (!tz) return -ENOMEM; /* 获取 SCPI sensor ID */ ret of_property_read_u32(np, arm,scpi-sensor-id, tz-scpi_id); if (ret) return ret; /* 注册 thermal zone传入 ops 结构体 */ tz-tzd devm_thermal_zone_of_sensor_register(pdev-dev, np, tz, imx8mq_sensor_ops); if (IS_ERR(tz-tzd)) { ret PTR_ERR(tz-tzd); dev_err(pdev-dev, failed to register thermal zone: %d\n, ret); return ret; } platform_set_drvdata(pdev, tz); return 0; }这里的关键是devm_thermal_zone_of_sensor_register()它会自动解析 DTS 里的 trips 和 cooling-maps并完成 binding。你不需要手动调用thermal_zone_bind_cooling_device()。而 CPU cooling device driver即 cpufreq driver必须在 thermal zone 注册前就准备好。标准做法是在cpufreq_driver_init()里注册static struct thermal_cooling_device *cpu_cooling_device; static int cpu_cooling_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { *state cpufreq_frequency_table_count(); // 返回频率档位总数 return 0; } static int cpu_cooling_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { /* state 是 0~N对应 cpufreq table 的索引 */ return cpufreq_driver_target(cpufreq_cpu_get(0), freq_table[state].frequency, CPUFREQ_RELATION_L); } static const struct thermal_cooling_device_ops cpu_cooling_ops { .get_max_state cpu_cooling_get_max_state, .set_cur_state cpu_cooling_set_cur_state, }; static int __init cpu_cooling_init(void) { cpu_cooling_device thermal_cooling_device_register(cpu0, NULL, NULL, cpu_cooling_ops); if (IS_ERR(cpu_cooling_device)) return PTR_ERR(cpu_cooling_device); return 0; }注意thermal_cooling_device_register(cpu0)的名称必须和 DTS 里的cpu0完全一致。4.4 启动验证用 sysfs 和 tracepoint 看清每一帧决策内核启动后验证 thermal framework 是否正常工作不能只看cat /sys/class/thermal/thermal_zone0/temp。必须用三层验证法第一层基础状态检查# 查看 zone 状态 cat /sys/class/thermal/thermal_zone0/type # 应输出 cpu-thermal cat /sys/class/thermal/thermal_zone0/mode # 应输出 enabled cat /sys/class/thermal/thermal_zone0/governor # 应输出 step_wise cat /sys/class/thermal/thermal_zone0/trips # 应列出 3 个 trip cat /sys/class/thermal/thermal_zone0/cdev0 # 应输出 cpu0 # 查看 cooling device 状态 cat /sys/class/thermal/cooling_device0/type # 应输出 Processor cat /sys/class/thermal/cooling_device0/max_state # 应输出频率档位数如 10 cat /sys/class/thermal/cooling_device0/cur_state # 当前 CPU 频率档位第二层模拟升温观察响应# 启用 emulation需 CONFIG_THERMAL_EMULATIONy echo 1 /sys/class/thermal/thermal_zone0/emul_temp # 设置模拟温度为 80℃单位 millidegree echo 80000 /sys/class/thermal/thermal_zone0/emul_temp # 实时监控温度和状态变化 watch -n 0.5 cat /sys/class/thermal/thermal_zone0/temp /sys/class/thermal/cooling_device0/cur_state正常现象温度稳定在 80000 后cur_state应逐步升高如从 0→3→5CPU 频率下降系统负载无明显卡顿。第三层tracepoint 深度分析# 开启 thermal tracepoint echo 1 /sys/kernel/debug/tracing/events/thermal/thermal_temperature/enable echo 1 /sys/kernel/debug/tracing/events/thermal/thermal_throttle/enable echo 1 /sys/kernel/debug/tracing/tracing_on # 触发一次被动降温 echo 76000 /sys/class/thermal/thermal_zone0/emul_temp # 查看 trace cat /sys/kernel/debug/tracing/trace_pipe你会看到类似这样的输出thermal-123 [001] d... 12345.678901: thermal_temperature: thermal_zone0: temp76000 thermal-123 [001] d... 12345.678902: thermal_throttle: thermal_zone0: trip1 (passive) triggered thermal-123 [001] d... 12345.678903: thermal_cooling: cpu0: set_cur_state4这证明温度读取 → trip 判断 → governor 决策 → cooling device 执行整条链路畅通无阻。实操心得我在调试 i.MX8MQ 时发现emul_temp只影响get_temp()返回值但不会触发硬件 sensor 的中断。所以如果你的 sensor driver 里用了中断模式如request_threaded_irq()必须同时注释掉中断处理部分否则emul_temp会和硬件读数竞争导致温度值乱跳。这是文档里绝不会写的坑。5. 常见问题与排查技巧实录从“温度不更新”到“governor 不生效”的硬核排障在真实项目中thermal framework 的问题往往不是“功能不存在”而是“功能在错误的时间、以错误的方式被调用”。下面是我整理的 7 个高频问题每个都附带 root cause 分析、复现步骤、和一行命令定位法。这些问题90% 的工程师在第一次移植 thermal 时都会撞上。5.1 问题/sys/class/thermal/thermal_zone0/temp始终为 0现象DTS 写了driver 也 probe 成功但cat temp永远返回 0。Root Causeget_temp()回调返回了错误的单位。thermal core 要求单位是millidegree Celsius千分之一摄氏度而很多 sensor driver 直接返回 raw ADC 值或 degree。复现步骤在get_temp()里加pr_info(raw%d\n, val);cat /sys/class/thermal/thermal_zone0/temp发现日志里raw256但 temp0定位命令# 查看 thermal core 的 debug log dmesg | grep -i thermal.*get_temp # 如果看到 get_temp returned -22说明返回值非法-22 是 EINVAL解决方案检查get_temp()的返回值。必须满足*temp -273150-273.15℃且*temp 175000175℃单位是 millidegree。例如硬件返回 25℃必须写*temp 25000;而不是*temp 25;。5.2 问题trip point 定义了但cat trips为空现象DTS 里明明写了trips { ... }但cat /sys/class/thermal/thermal_zone0/trips输出 nothing。Root CauseDTS 节点名不匹配。thermal core 要求 thermal zone 的 node name 必须是xxx-thermal带-thermal后缀否则of_thermal.c里的thermal_of_populate_trip_points()函数会跳过解析。复现步骤DTS 写cpu_thermal: cpu { ... };缺少-thermal启动后ls /sys/class/thermal/看到thermal_zone0但cat trips为空定位命令# 查看内核启动时是否解析了 trips dmesg | grep -i thermal.*trip # 正常应有 Parsed 3 trip points # 如果没有检查 dts node name解决方案把cpu { ... };改成cpu-thermal { ... };重新编译 dtb。5.3 问题echo passive /sys/class/thermal/thermal_zone0/governor失败返回 Invalid argument现象cat /sys/class/thermal/thermal_zone0/available_governors显示passive但 echo 就报错。Root Causepassivegovernor 在 5.4 内核中已被移除替换为step_wise。passive是旧名新内核只认step_wise。复现步骤在 5.10 内核上echo passive /sys/class/thermal/thermal_zone0/governor返回-EINVAL定位命令# 查看内核版本和 config uname -r zcat /proc/config.gz | grep THERMAL_GOV # 如果看到 CONFIG_THERMAL_GOV_STEP_WISEy但没看到 PASSIVE则 confirm解决方案echo step_wise /sys/class/thermal/thermal_zone0/governor。step_wise就是passive的继任者行为完全一致。5.4 问题cooling device 绑定了但cat /sys/class/thermal/cooling_device0/cur_state始终为 0现象cat cdev0显示cpu0但cur_state不随温度变化。Root Causecooling device 的set_cur_state()回调里cpufreq_driver_target()调用失败但 driver 没有返回 error导致 thermal core 认为设置成功。复现步骤在set_cur_state()里加pr_err(set state %ld\n, state);升高模拟温度发现日志有输出但cur_state不变定位命令# 查看 cpufreq 是否正常工作 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq # 如果始终为 0说明 cpufreq driver 未初始化 # 检查 cpufreq driver 是否 probe 成功 dmesg | grep -i cpufreq解决方案确保 cpufreq driver 在 thermal zone probe 前已加载。在 DTS 中把cpufreq节点放在thermal-zones节点之前或在 thermal driver 的probe()里加msleep(100)等待 cpufreq ready。5.5 问题CRITICALtrip 触发后系统 shutdown但/var/log/kern.log没有 shutdown 记录现象设备突然断电串口 log 在critical temperature reached后戛然而止。Root CauseCRITICAL触发时内核调用emergency_shutdown()该函数会直接调用machine_power_off()不经过 userspace 的 logger。所以 kern.log 截断是正常的。复现步骤echo 96000 /sys/class/thermal/thermal_zone0/emul_temp系统立即断电log 停在shutting down定位命令# 在触发前打开 thermal trace echo 1 /sys/kernel/debug/tracing/events/thermal/thermal_critical/enable # 触发后cat /sys/kernel/debug/tracing/trace_pipe会看到 critical event解决方案这不是 bug是设计。生产环境必须确保CRITICAL温度留有足够余量≥5℃并在PASSIVE阶段就介入调控绝不能依赖CRITICAL。5.6 问题step_wisegovernor 下cur_state跳变剧烈0→5→0→5现象温度缓慢上升但cur_state在两级间疯狂切换。Root Causehysteresis滞后值设置过小。step_wise的状态切换逻辑是温度 trip[i].temp → 升一级温度 trip[i].temp - hysteresis
企业数字化 ERP 产品动态
相关推荐
ESP32-S3 Mini还是C3 Mini?PSRAM与USB差异详解 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 4:32:41
多机器人协同导航仿真:ROS2与Gazebo动态编队实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/9 3:37:28
马鞍山当涂电工证报名值不值得考?跨省转证怎么处理 马鞍山当涂电工证报名值不值得考?跨省转证怎么处理 之前考的证在外省能不能转过来?这几乎是每个打算考电工证的朋友都会问的问题。尤其对于已经在外地工作、但想回到马鞍山当涂发展的朋友来说,这个问题更显得重要。今天我们就围绕【马鞍山当涂电工证报名】… · 2026/11/11 14:13:03
兰考考电工证考前押题怎么找?转岗电工必看的避坑指南 兰考考电工证考前押题怎么找?转岗电工必看的避坑指南 你是不是正在为找工作发愁,听说电工证是必备条件,却不知道兰考考电工证到底怎么操作?别急,这篇文章帮你理清楚流程,还附上考前押题攻略和避坑经验,专为想转岗的你准备。… · 2026/11/11 14:13:03
电工证异地复审代办保姆级教程:怕考不过白交培训费?这些细节你必须知道 电工证异地复审代办保姆级教程:怕考不过白交培训费?这些细节你必须知道 怕考不过白交培训费?别急,我这有套保姆级教程,专治备考焦虑。电工证异地复审代办这事,说白了就是“考过不丢人,考不过就亏本”。今天我从郑州金水区公路工程从业者的角度,给你讲… · 2026/11/11 14:13:03
兰考考电工证怎么报名?从零到上岗全攻略 兰考考电工证怎么报名?从零到上岗全攻略 你是不是也这样?手里拿着简历,却因为没有特种作业操作证被拒之门外?别急,兰考考电工证正是你打开新机会的钥匙。电工证是从事电气作业的“通行证”,特别是在建筑工地、工厂、配电房等场景,没有证根本无法上岗。… · 2026/11/11 14:13:03
阳朔县电工证上岗必须要这个证吗?网上查询发现考前准备太关键 阳朔县电工证上岗必须要这个证吗?网上查询发现考前准备太关键 实操考试心里没底怕挂科?阳朔县电工证的报考人数每年都在增长,但真正能顺利通过考试的却不多。不少考生在考前临时抱佛脚,结果因为没掌握好实操技巧,最终挂科。今天就用一个真实的案例来聊聊… · 2026/11/11 14:13:02
清远建筑电工证办理保姆级教程:工地太忙根本没时间复习考试? 清远建筑电工证办理保姆级教程:工地太忙根本没时间复习考试? 工地太忙根本没时间复习考试?清远建筑电工证办理流程复杂、考试内容难记,很多一线工人和管理人员都因此头疼。今天就从南阳卧龙区项目现场管理员的实际角度出发,手把手带你了解清远建筑电工证… · 2026/11/11 14:12:02
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/11 12:18:07
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/10 17:46:39
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/10 17:11:43
_中职电工证最新政策解读 中职电工证在哪考?工地太忙根本没时间复习考试的救星来了 工地太忙根本没时间复习考试,这是很多正在备考 中职电工证 的建筑工友的真实写照。你不是不想考,而是真的没时间。但别担心,这期内容是 新乡初次报考人员… · 2026/11/11 0:00:52
昆明电工证培训价格值不值得考?老电工告诉你真相 昆明电工证培训价格值不值得考?老电工告诉你真相 之前考的证在外省能不能转过来?这个问题是很多在外地打工、换城市生活、或者是想在昆明发展的朋友最关心的。尤其是现在云南发展快,很多企业都在招持证上岗的电工,如果你手头有证但不在本地,那这个证是不… · 2026/11/11 0:00:52
特种电缆张工网上查询全攻略:外省证如何转过来 特种电缆张工网上查询全攻略:外省证如何转过来 之前考的证在外省能不能转过来?这是很多特种作业从业者最关心的问题,尤其是像特种电缆张工这类证书,直接关系到日常工作是否合规、能否顺利晋升。很多小伙伴因为证书归属地的问题,陷入职业发展的瓶颈,甚至… · 2026/11/11 0:00:52
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 20:19:29
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 19:22:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/10 17:00:47