ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux thermal framework 通用架构解析:zone、cooling device 与 governor 实战

Linux thermal framework 通用架构解析:zone、cooling device 与 governor 实战 1. 功耗子系统里的“温控管家”thermal framework 到底管什么搞嵌入式或者做移动端内核的兄弟对“发热降频”这四个字肯定不陌生。手机玩久了烫手、平板看视频卡顿、车机夏天暴晒后反应迟钝背后往往都有一只看不见的手在操控——Linux 内核里的thermal framework。这个框架不直接参与运算也不直接调度进程但它决定了你的 SoC 在温度爬升时是“优雅地慢下来”还是“一头撞上温度墙直接重启”。我接触 thermal 这块大概是从做一款国产平板项目开始的当时机器跑满载测试十分钟不到就烫得没法握系统日志里全是thermal_zone的告警从那时候起才真正把 thermal framework 的通用架构啃了一遍。简单说thermal framework 是 Linux 内核功耗子系统中的一个子模块核心职责就三件事感知温度、制定策略、执行降温。它把温度传感器、降温设备、控制策略这三类东西抽象成统一的模型让驱动开发者不用为每一颗芯片重写一套温控逻辑。适合谁来参考如果你正在做嵌入式 Linux 驱动开发、功耗优化、或者单纯想搞明白“为什么我的板子一热就卡”这套架构梳理都能直接拿去用。下面我按自己踩坑的顺序把 thermal framework 的通用架构从设计思路到实操细节完整拆一遍。2. 通用架构的整体设计思路拆解2.1 为什么内核要单独搞一个 thermal 子系统早期内核里温控是散落在各个平台代码里的每个 SoC 厂商自己写一套读温度、判断、降频的流程代码重复不说策略还很难复用。后来内核维护者把这些共性抽出来形成了 thermal framework。它的设计哲学很像一个“中介平台”上游对接各种温度传感器下游对接各种降温手段中间用统一的策略层做决策。这个思路的好处很直接。第一解耦。传感器驱动只管上报温度不用关心降频怎么降降温设备驱动只管执行动作不用关心温度从哪来。第二可复用。同一套策略代码换一颗传感器或者换一个风扇驱动几乎不用改。第三可观测。所有温度区、降温设备、策略都在 sysfs 里有对应节点调试的时候直接 cat 就能看状态不用加一堆 printk。我个人的体会是理解 thermal framework 最关键的是抓住它的“三件套”模型thermal zone温度区、thermal cooling device降温设备、thermal governor调控策略。这三者之间的关系就是整个通用架构的骨架。2.2 三件套模型zone、cooling device、governorthermal zone可以理解为一个“温度观测点”。比如 CPU 大核有一个温度区GPU 有一个温度区电池有一个温度区。每个 zone 会绑定一个或多个温度传感器并持有一个当前温度值。zone 还负责触发策略判断是温控的入口。thermal cooling device是“降温执行器”。最常见的就是 CPU 调频cpufreq cooling、GPU 调频、风扇开关、甚至直接关核。每个 cooling device 会暴露一个“降温能力等级”比如 CPU 可以降 0 到 10 档档位越高降温越狠。thermal governor是“决策大脑”。它根据 zone 的温度和预设的触发点trip point决定让哪些 cooling device 降到什么档位。内核里常见的 governor 有step_wise、power_allocator、fair_share、bang_bang等不同场景选不同策略。这三者的关系用一句话概括zone 报温度governor 做决策cooling device 执行动作。整个 thermal framework 的代码结构基本就是围绕这三者的注册、绑定和回调展开的。2.3 设备树里的 thermal 描述方式现在主流平台都用设备树Device Tree来描述 thermal 硬件。一个典型的 thermal zone 节点大概长这样thermal-zones { cpu_thermal: cpu-thermal { polling-delay-passive 250; polling-delay 1000; thermal-sensors tsadc 0; trips { cpu_alert: cpu-alert { temperature 70000; hysteresis 2000; type passive; }; cpu_crit: cpu-crit { temperature 95000; hysteresis 2000; type critical; }; }; cooling-maps { map0 { trip cpu_alert; cooling-device cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT; }; }; }; };这里有几个关键点值得展开。polling-delay是轮询间隔单位毫秒被动模式下通常设 250ms 左右太短浪费 CPU太长响应不及时。trips是触发点type分passive被动降频、active主动散热如风扇、critical临界关机、hot仅告警。cooling-maps把 trip 和 cooling device 绑起来THERMAL_NO_LIMIT表示不限制档位范围。注意hysteresis是迟滞值防止温度在触发点附近抖动导致频繁升降频。我见过不少项目因为没设迟滞温度一到 70 度就降频降到 69 度又升回来来回震荡体验极差。3. 核心细节解析与实操要点3.1 thermal zone 的注册与温度上报流程thermal zone 的注册一般分两种方式一种是通过设备树自动解析生成另一种是驱动里手动调用thermal_zone_device_register()。现在新平台基本都走设备树驱动只需要实现温度读取回调。温度上报的核心是get_temp回调。zone 在轮询时会调用这个回调拿到当前温度然后更新内部状态并触发 governor 判断。这里有个容易踩的坑回调里不能做耗时操作。我见过有人在get_temp里通过 I2C 读传感器结果 I2C 总线一忙整个 thermal 轮询线程就被阻塞温度更新延迟好几秒降频完全来不及。正确的做法是把传感器读数缓存起来get_temp只返回缓存值实际读取放在中断或者独立的工作队列里。另外温度单位统一用毫摄氏度也就是 70 度要写成 70000这个在调试时经常有人搞错导致触发点完全不对。3.2 cooling device 的档位设计与绑定cooling device 的核心是get_max_state和set_cur_state两个回调。get_max_state返回最大档位数set_cur_state执行档位切换。以 cpufreq cooling 为例它会把 CPU 的频率表映射成档位档位越高频率越低。绑定关系通过cooling-maps建立。一个 trip 可以绑定多个 cooling device一个 cooling device 也可以被多个 trip 绑定。这里的设计意图是支持“组合降温”比如温度到 70 度先降 CPU到 80 度再开风扇到 90 度连 GPU 一起降。实操中我建议档位不要设太细。有些平台把 CPU 频率分成十几档结果 governor 每次只降一档温度半天降不下来。一般 4 到 6 档比较合适既能平滑过渡又能快速响应。3.3 governor 策略的选择逻辑内核自带的 governor 各有适用场景选错了要么降温不及时要么频繁抖动。下面这张表是我自己整理的经验对照governor适用场景特点注意事项step_wise通用场景每次降一档逐步逼近响应偏慢适合温度变化平缓的设备bang_bang简单开关超过触发点直接最大档容易震荡需配合迟滞fair_share多设备均衡按权重分配降温档位配置复杂适合多核多设备power_allocator移动端基于功耗预算动态分配需要准确的功耗模型参数我个人的经验是移动端优先用 power_allocator因为它能根据当前功耗预算动态调整不会一刀切降频。固定设备比如路由器、工控板用 step_wise就够了简单可靠。bang_bang我只在风扇控制这种二值场景用过温度控制场景基本不碰。3.4 trip point 的温度阈值怎么定trip point 的温度设定直接决定用户体验。设太低机器稍微一忙就降频性能上不去设太高温度压不住可能触发 critical 关机。我的经验值是passive 触发点比芯片规格书里的最高结温低 20 到 25 度。比如结温 105 度passive 设 80 到 85 度。critical 触发点比最高结温低 5 到 10 度留出关机保护余量。hysteresis一般设 2 到 5 度温度变化快的场景取大值。这些值不是拍脑袋定的最好结合实测。我一般会先用默认值跑一遍满载测试用thermal_zone的 sysfs 节点记录温度曲线看降频点是否合理再微调。4. 实操过程与核心环节实现4.1 从零配置一个 thermal zone 的完整步骤假设你拿到一块新板子传感器是 I2C 接口的CPU 支持 cpufreq要配一个完整的温控链路。我按实际项目顺序走一遍。第一步确认传感器驱动已经加载并且注册了 thermal sensor。可以用ls /sys/class/thermal/看有没有thermal_zone*节点。如果没有说明设备树里没配或者驱动没起来。第二步在设备树里添加 thermal zone 节点绑定传感器和 cooling device。这里要注意thermal-sensors的 phandle 必须和传感器驱动里的一致否则 zone 注册会失败。第三步配置 trips 和 cooling-maps。trip 的type要和 governor 匹配比如step_wise需要passive类型的 trip。第四步编译设备树重启检查/sys/class/thermal/thermal_zone0/下是否有temp、trip_point_0_temp、policy等节点。第五步手动触发测试。可以用stress或者dd制造负载同时用watch -n 1 cat /sys/class/thermal/thermal_zone0/temp观察温度变化确认降频动作是否按预期执行。4.2 关键参数计算polling-delay 与响应时间polling-delay的设定需要权衡响应速度和 CPU 开销。假设你的温度上升速率是每秒 2 度passive 触发点是 80 度当前温度 75 度那么距离触发还有 2.5 秒。如果polling-delay设 1000ms最多延迟 1 秒发现还能接受如果设 2000ms可能发现时已经 79 度再降频就有点晚。计算公式可以简化为polling-delay ≤ (触发点 - 当前温度) / 温升速率 × 安全系数。安全系数一般取 0.5。按上面的例子就是 2.5 × 0.5 1.25 秒取 1000ms 比较合适。被动模式下我一般设 250ms 到 500ms主动模式有风扇可以放宽到 1000ms因为风扇响应本身有延迟。4.3 用 sysfs 做运行时调试的实操记录thermal framework 的 sysfs 接口是调试利器。我常用的几个节点# 查看当前温度毫摄氏度 cat /sys/class/thermal/thermal_zone0/temp # 查看当前策略 cat /sys/class/thermal/thermal_zone0/policy # 切换策略 echo power_allocator /sys/class/thermal/thermal_zone0/policy # 查看触发点 cat /sys/class/thermal/thermal_zone0/trip_point_0_temp cat /sys/class/thermal/thermal_zone0/trip_point_0_type # 查看 cooling device 状态 cat /sys/class/thermal/cooling_device0/cur_state cat /sys/class/thermal/cooling_device0/max_state有一次项目里温度一直不降我 cat 了cur_state发现 cooling device 一直是 0说明 governor 根本没触发。后来查出来是 trip 的type写成了active但 governor 是step_wise不匹配导致策略没生效。改成passive后立刻正常。这种问题看日志很难发现sysfs 一看就明白。提示调试阶段可以把polling-delay临时调小加快观察节奏但量产固件里一定要改回合理值否则会增加功耗。5. 常见问题与排查技巧实录5.1 温度不更新或更新异常这是最常见的问题。表现是temp节点一直不变或者跳变很大。排查思路先确认传感器驱动是否正常 probedmesg | grep thermal看有没有报错。检查get_temp回调是否被调用可以临时加计数打印。如果是 I2C 传感器用i2cdetect确认地址正确用i2cget直接读寄存器看原始值。温度跳变通常是滤波没做好可以在驱动里加滑动平均。我遇到过一次温度一直显示 0最后发现是设备树里thermal-sensors的 phandle 指向了一个没使能的节点驱动根本没绑定上。5.2 降频不生效或降频过度降频不生效先看 cooling device 的cur_state有没有变化。如果一直是 0说明 governor 没下发指令检查 trip 配置和 governor 类型是否匹配。如果cur_state变了但频率没变说明 cpufreq cooling 的映射有问题检查 CPU 频率表是否被正确注册。降频过度通常是 trip 温度设太低或者 governor 太激进。可以适当提高触发点或者换用step_wise这种渐进策略。另外检查是否有多个 zone 同时触发同一个 cooling device导致叠加降频。5.3 系统频繁触发 critical 关机critical 关机是最后一道保护频繁触发说明前面的 passive 降温没起作用。排查顺序先看 passive trip 是否触发再看 cooling device 是否真的在降温最后看散热设计是否本身就不足。我见过一个项目CPU 满载功耗 8W但散热片只按 5W 设计passive 降频到最低档温度还是压不住最后只能改硬件。5.4 常见问题速查表现象可能原因排查方法解决方向温度不更新驱动未 probe / phandle 错误dmesg、sysfs 节点修设备树或驱动温度跳变无滤波 / 读取阻塞加打印看原始值加滑动平均、异步读取降频不生效trip 类型不匹配看 cur_state改 trip type 或 governor降频过度触发点太低看 trip_point_temp提高阈值频繁 critical散热不足看温度曲线改硬件或加主动散热策略切换无效governor 未编译进内核看 policy 节点开对应 config5.5 几个只有踩过才知道的坑第一个坑thermal zone 名字不能重复。设备树里两个 zone 如果用了同一个 label注册时会失败但报错信息很隐晦只提示-EEXIST不告诉你是哪个重复了。我建议命名带上具体位置比如cpu0-thermal、gpu-thermal。第二个坑cooling device 的档位是反的。cpufreq cooling 里档位越高频率越低但有些驱动实现是反的导致 governor 以为在降温实际在升频。调试时一定要实测频率变化。第三个坑power_allocator 需要配置sustainable-power。这个参数不配的话默认值可能完全不适合你的平台导致降温策略失效。这个值一般取平台长期稳定运行的功耗比如 3W 到 5W。第四个坑thermal 线程优先级。thermal 的轮询线程默认优先级不高如果系统负载很重可能被其他线程挤占导致温度更新延迟。可以在驱动里适当提高优先级但别设成实时优先级否则可能影响系统稳定性。6. 从通用架构到具体平台的适配思路6.1 不同平台的 thermal 差异点虽然 thermal framework 是通用的但不同平台在实现上有差异。ARM 平台通常用thermal_zone加cpufreq coolingx86 平台更多用intel_powerclamp和acpi thermal。国产 SoC 比如瑞芯微、全志基本都走设备树加标准框架但传感器驱动和 cooling device 实现各有不同。适配新平台时我一般先看厂商有没有提供参考设备树配置有的话直接拿来改没有的话就按标准流程从传感器驱动开始配。重点是确认传感器的温度换算公式有些传感器原始值是 12 位 ADC需要按规格书换算成毫摄氏度算错了整个温控就全乱。6.2 多 zone 协同的配置要点复杂 SoC 往往有多个温度区比如大核、小核、GPU、NPU 各一个。多 zone 协同的关键是避免降温冲突。如果两个 zone 都绑定了同一个 cooling device一个要降 3 档一个要降 5 档最终会取最严格的那个可能导致过度降频。我的做法是给不同 zone 分配不同的 cooling device或者用fair_sharegovernor 做权重分配。另外zone 之间的温度触发点要拉开梯度比如大核 75 度触发GPU 80 度触发避免同时触发。6.3 功耗与性能的平衡取舍thermal 调优本质上是功耗、性能、温度三者的平衡。降频越狠温度越低但性能损失越大。我的经验是优先保证不触发 critical其次保证长时间负载下性能稳定最后才追求峰值性能。具体做法是设两级 passive trip第一级轻度降频比如降 1 到 2 档让温度缓慢回落第二级重度降频降 4 到 5 档快速压温。这样短时间负载不会明显掉性能长时间负载也能稳住温度。实测下来这种两级策略比单级策略的用户体验好很多尤其是游戏和视频场景。6.4 实测数据记录与调优闭环调优不能靠感觉要有数据。我一般用脚本记录温度、频率、负载三组数据跑 30 分钟满载然后画曲线看降频点和温度回落情况。如果温度在触发点附近震荡就加迟滞如果降频后温度还是上升就提前触发点或者加大降频档位。这个闭环我跑了大概十几个项目基本三到五轮就能调到比较理想的状态。关键是每次只改一个参数改完重新测否则多个变量一起动根本不知道哪个起了作用。7. 我个人在实际操作中的几点体会thermal framework 这套架构刚看代码的时候觉得节点多、回调多容易晕。但抓住 zone、cooling device、governor 这三件套再顺着设备树的配置走一遍基本就能理清。我建议新手先从 sysfs 入手手动改 policy、看 cur_state把整个链路跑通再回头看代码会顺畅很多。另外thermal 调优没有一劳永逸的参数不同板子、不同散热设计、不同使用场景最优值都不一样。我一般会在项目里留一份调优记录写清楚每个参数的设定理由和实测效果下次遇到类似平台可以直接参考省很多时间。最后分享一个小技巧如果调试时不确定 governor 有没有在工作可以把polling-delay设成 100ms然后用ftrace抓thermal_zone_device_update的调用看每次温度更新后有没有触发 governor 回调。这个方法比看日志直观得多能快速定位是传感器问题还是策略问题。
返回列表