linux 如何读取的cpu 温度? (真实平台)


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%对齐官方驱动版");
相关推荐
谷雨不太卷11 小时前
进程如何加载文件
linux·运维·服务器
shandianchengzi12 小时前
【记录】LosslessCut|Linux下配置开源无损剪辑软件 LosslessCut AppImage 命令行启动和设置图标
linux·运维·服务器·音视频·视频·剪辑
小猫咪0112 小时前
Linux 查看端口占用:netstat、ss、lsof 谁更好用?
linux·运维·网络
都在酒里12 小时前
Linux字符设备驱动开发(八):中断底半部——tasklet与工作队列实现按键消抖
linux·运维·驱动开发·交互
cui_ruicheng12 小时前
Linux网络编程(十):自定义协议与网络计算器
linux·服务器·网络·tcp/ip
开开心心就好12 小时前
180套模板的图片艺术拼接实用工具
linux·服务器·网络·spring·智能手机·maven·excel
Strugglingler12 小时前
Linux Device Drivers-第十章 中断处理
linux·irq·工作队列·tasklet·中断共享
程序leo源12 小时前
Qt界面优化详解
linux·c语言·开发语言·c++·qt·c#
tang74516396212 小时前
Ubuntu 24.04 安装 Nginx 1.29.6 完整版教程20260320
linux·nginx·ubuntu