author: hjjdebug
date: 2026年 05月 28日 星期四 11:36:20 CST
descrip: linux 如何读取的cpu 温度? (真实平台)
文章目录
- [1. 读取温度.](#1. 读取温度.)
-
- [1.1 读取测试点参考温度](#1.1 读取测试点参考温度)
- [1.2 读取测试点温差温度](#1.2 读取测试点温差温度)
- [1.3 计算实际温度.](#1.3 计算实际温度.)
- [2. 注册一个热区 :](#2. 注册一个热区 :)
- [3. 测试](#3. 测试)
- [4. 驱动源码 (82行)](#4. 驱动源码 (82行))
前面曾经写过一个虚拟平台读取cpu 温度, 只要完善读取温度函数,从
真实机器上读取温度, 那就是真正的cpu温度.
完整驱动代码见后面附录
关键点梳理:
1. 读取温度.
通过读取某个特定寄存器来读取到温度.
具体过程稍微复杂一点. 先读取一个参考值, 再读取到参考值的差值计算出温度.
1.1 读取测试点参考温度
#define MSR_IA32_TEMPERATURE_TARGET 0x1A2
MSR : model specified register, 模型特定寄存器
IA32_TEMPERATURE_TARGET:Intel 统一命名,地址固定 0x1A2
作用: 该地址保存了 CPU 出厂烧录的温度阈值,或者说温度参考值, 或者说最大温度值. 一般是100或105
所有测量的温度是以该值为参考的.
特定的寄存器, 只能用特定的指令来读取.
rdmsrl(MSR_IA32_TEMPERATURE_TARGET, msr_target);
读取参考值到msr_target, 计算出最大温度tj_max
tj 就是test junction, 测试结点的意思
1.2 读取测试点温差温度
#define MSR_IA32_PACKAGE_THERM_STATUS 0x1B1
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_status);
读取cpu 温度温度到 msr_status.
读取的数值并不直接代表温度值, 而是到参考值的差值.
1.3 计算实际温度.
所以温度是tj_max-tj_offset, 看看代码就一目了然.
这个地址0x1b1 决定了读取的是哪里的温度值.
2. 注册一个热区 :
怎样执行到我们的驱动代码? 需要在内核上开一个窗口.
这需要注册一个热区.
intel_cpu_tz = thermal_zone_device_register(
"intel_cpu_real", //类型名称,自定义
0,
0,
NULL,
&intel_tz_ops, //操作函数
&intel_tz_params, //操作函数参数
0,
2000 //polling_delay,查询间隔
);
该函数会在内核上开一个窗口,使用户通过窗口访问到内部驱动.
这个窗口就是/sysfs 文件. 具体为:
/sys/devices/virtual/thermal/thernal_zoneX
首先, 它是开在/devices/virtual 总线下, 因为它不具体对应设备,所以在virtual 线下就很合适.
第2,它属于热区,故下级目录为thermal, 这两层目录是接口函数决定的.
thermal_zoneX, 其中这个X 是代表的是zone编号. 由系统根据当前的zone个数来确定.
函数中的参数意义也已经注明.
如果我不这样开口子,而是注册一个cdev, 在/dev/创建一个字符设备, 也是可以的.
不过内核为热区专门提供了函数接口thermal_zone_device_register 函数, 用它就更方便了.
这样也了解到了其它thermal_zone 是怎么来的.
3. 测试
$ insmod intel_minimal_temp.ko
$ lsmod
$ dmesg
15031.287916 ✅ 本机CPU真实TJ Max临界温度 = 105 °C
15031.287995 ✅ 精准Intel CPU测温驱动加载完成
15031.287997 👉 读取温度命令: cat /sys/class/thermal/thermal_zoneX/temp
insmod 后,我们发现在/sys/class/thermal/下多出了thermal_zone4, 那就进入到该目录
hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat temp
51000
还发现,我们新添的这个zone4 跟 zone3 的数值是一致的,
这说明,我们的驱动是正确的, 而且它就等价于系统zone3.
至此,消除了cpu温度测量的神秘性. 就是读一个MSR 来实现的. 而展现它,用thermal_zone来展示.
我们也能写完整的驱动.
附件代码给出了完整实现.
4. 驱动源码 (82行)
cpp
$ cat intel_minimal_temp.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/thermal.h>
#include <asm/msr.h>
// Intel 官方温度寄存器定义
//#define MSR_IA32_PACKAGE_THERM_STATUS 0x1B1
//#define MSR_IA32_TEMPERATURE_TARGET 0x1A2
static int tj_max = 100; // 全局存储本机真实临界温度
static int intel_real_get_temp(struct thermal_zone_device *tz, int *temp)
{
u64 msr_status;
int tj_offset;
// 读取CPU当前热状态
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_status);
// 提取距离过热临界点的剩余度数
tj_offset = (msr_status >> 16) & 0xFF;
// 用CPU原生真实TJ_MAX精准计算毫摄氏度
*temp = (tj_max - tj_offset) * 1000;
return 0;
}
static struct thermal_zone_device_ops intel_tz_ops = {
.get_temp = intel_real_get_temp, //实现查看温度函数
};
static struct thermal_zone_params intel_tz_params = {
.no_hwmon = true,
};
static struct thermal_zone_device *intel_cpu_tz;
static int __init intel_temp_init(void)
{
u64 msr_target;
// 第一步:自动读取CPU出厂自带的真实TJ Max临界温度
rdmsrl(MSR_IA32_TEMPERATURE_TARGET, msr_target);
tj_max = (msr_target >> 16) & 0xFF;
pr_info("✅ 本机CPU真实TJ Max临界温度 = %d °C\n", tj_max);
// 严格适配 Linux 5.4 内核 完整8参数原型,注册一个热区
intel_cpu_tz = thermal_zone_device_register(
"intel_cpu_real",
0,
0,
NULL,
&intel_tz_ops,
&intel_tz_params,
0,
2000 //polling_delay
);
if (IS_ERR(intel_cpu_tz)) {
pr_err("❌ 温控域注册失败\n");
return PTR_ERR(intel_cpu_tz);
}
pr_info("✅ 精准Intel CPU测温驱动加载完成\n");
pr_info("👉 读取温度命令: cat /sys/class/thermal/thermal_zoneX/temp\n");
return 0;
}
static void __exit intel_temp_exit(void)
{
thermal_zone_device_unregister(intel_cpu_tz);
pr_info("✅ 驱动已安全卸载\n");
}
module_init(intel_temp_init);
module_exit(intel_temp_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("自动TJ Max 100%对齐官方驱动版");