linux 内核, 怎样读取温度?


author: hjjdebug

date: 2026年 05月 26日 星期二 17:15:39 CST

descrip: linux 内核, 怎样读取温度?


文章目录

  • [1. 添加platrform设备](#1. 添加platrform设备)
  • [2. 注册platform驱动,](#2. 注册platform驱动,)
  • [3. platform设备是怎样找到它的驱动的 ?](#3. platform设备是怎样找到它的驱动的 ?)
  • [4. 热区,温度区. thermal_zone.](#4. 热区,温度区. thermal_zone.)
  • [5. 小结:](#5. 小结:)
  • [6. 测试代码](#6. 测试代码)
  • [7. 简化后的测试代码:](#7. 简化后的测试代码:)

目的: 以最简单的温度测量为例, 了解一下linux下的驱动架构模型.
写到最后发现,附录2是最简单的,附录1还挂载了platform设备,platform驱动框架.

温度,首先要有一个测温设备, 这里我们没有具体的测温设备,假想用一个platfrom设备.

我们假想用platform 设备,

有设备则必有驱动, 来与设备进行数据交互, 这里用platform 驱动来对应platform设备

platform 设备是用来测温度的, 所以驱动中可用 内置的thermal-zone 设备来读取.

编译成.ko

执行:

$ insmod virtual_thermal.ko

查看打印信息:

$ dmesg

28904.713790 virt_thermal: probe

28904.713841 virt_thermal: register ok

进一步查看系统目录及文件变化

1. 添加platrform设备

virt_dev = platform_device_alloc("virtual-thermal", -1);

platform_device_add(virt_dev); //添加了一个platform device, 名字叫virtual-thermal

查看/sysfs 文件系统:

/sys/devices/platform/ 目录下新增加一个virtual-thermal 目录

/sys/devices/platform/virtual-thermal$ ls -l

总用量 0

lrwxrwxrwx 1 root root 0 5月 26 15:53 driver -> .../.../.../bus/platform/drivers/virtual-thermal

-rw-r--r-- 1 root root 4096 5月 26 16:09 driver_override

-r--r--r-- 1 root root 4096 5月 26 16:09 modalias

drwxr-xr-x 2 root root 0 5月 26 16:09 power

lrwxrwxrwx 1 root root 0 5月 26 16:09 subsystem -> .../.../.../bus/platform

-rw-r--r-- 1 root root 4096 5月 26 15:53 uevent

hjj@hjj-laptop:/sys/devices/platform/virtual-thermal$ cat driver_override

(null)

hjj@hjj-laptop:/sys/devices/platform/virtual-thermal$ cat modalias

platform:virtual-thermal

hjj@hjj-laptop:/sys/devices/platform/virtual-thermal$ cat uevent

DRIVER=virtual-thermal

MODALIAS=platform:virtual-thermal

有一个子系统链接指向子系统 bus/platform

匹配成功后

有一个driver链接指向对应的driver 目录

2. 注册platform驱动,

static struct platform_driver virt_driver = {

.probe = virt_probe,

.remove = virt_remove,

.driver = {

.name = "virtual-thermal",

},

};

platform_driver_register(&virt_driver); //注册了一个platform driver,叫virtual-thermal 与device 匹配

查看/sysfs 文件系统:

/sys/bus/platform/drivers/ 目录下新增加一个virtual-thermal 目录

hjj@hjj-laptop:/sys/bus/platform/drivers/virtual-thermal$ ls -l

总用量 0

drwxr-xr-x 2 root root 0 5月 26 15:53 ./

drwxr-xr-x 15 root root 0 5月 26 07:35 .../

--w------- 1 root root 4096 5月 26 15:57 bind

lrwxrwxrwx 1 root root 0 5月 26 15:57 module -> .../.../.../.../module/virtual_thermal/

--w------- 1 root root 4096 5月 26 15:53 uevent

--w------- 1 root root 4096 5月 26 15:57 unbind

lrwxrwxrwx 1 root root 0 5月 26 15:57 virtual-thermal -> .../.../.../.../devices/platform/virtual-thermal/

有一个module 链接指向 driver 的module 目录

匹配成功后, 有一个virtual-thermal 链接指向driver的设备链接

3. platform设备是怎样找到它的驱动的 ?

复制代码
靠名称匹配. 驱动叫virtual-thermal, 设备也叫virtual-thermal, 这是最原始的一种匹配方式

4. 热区,温度区. thermal_zone.

复制代码
virt_tz = thermal_zone_device_register(  //注册一个thremal_zone,热区,温度区域
    "virtual_thermal",    // 1 type
    0,                    // 2 trips
    0,                    // 3 mask
    NULL,                 // 4 devdata
    &virt_ops,            // 5 ops
    &virt_tzp,            // 6 tzp
    0,                    // 7 passive_delay
    1000                  // 8 polling_delay (1秒)
);

thermal_zone 是指一个温度监控对象.

thermal_zone_device_register , 向内核温度子系统(thermal 子系统)

注册一个热区设备. 这样就可以在/sys/class/thermal/ 目录下生成对应的节点. 查是thermal_zone4.

lrwxrwxrwx 1 root root 0 5月 26 16:30 thermal_zone4 -> .../.../devices/virtual/thermal/thermal_zone4/

这个节点对应的设备: /sys/devices/virtual/thermal/thermal_zone4

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ ls -l

总用量 0

drwxr-xr-x 4 root root 0 5月 26 16:30 ./

drwxr-xr-x 16 root root 0 5月 26 07:35 .../

-r--r--r-- 1 root root 4096 5月 26 16:33 available_policies

drwxr-xr-x 3 root root 0 5月 26 16:30 hwmon3/

-rw-r--r-- 1 root root 4096 5月 26 16:33 integral_cutoff

-rw-r--r-- 1 root root 4096 5月 26 16:33 k_d

-rw-r--r-- 1 root root 4096 5月 26 16:33 k_i

-rw-r--r-- 1 root root 4096 5月 26 16:33 k_po

-rw-r--r-- 1 root root 4096 5月 26 16:33 k_pu

-rw-r--r-- 1 root root 4096 5月 26 16:33 offset

-rw-r--r-- 1 root root 4096 5月 26 16:33 passive

-rw-r--r-- 1 root root 4096 5月 26 16:33 policy

drwxr-xr-x 2 root root 0 5月 26 16:33 power/

-rw-r--r-- 1 root root 4096 5月 26 16:33 slope

lrwxrwxrwx 1 root root 0 5月 26 16:30 subsystem -> .../.../.../.../class/thermal/

-rw-r--r-- 1 root root 4096 5月 26 16:33 sustainable_power

-r--r--r-- 1 root root 4096 5月 26 16:33 temp

-r--r--r-- 1 root root 4096 5月 26 16:33 type

-rw-r--r-- 1 root root 4096 5月 26 16:30 uevent

这么多文件可读写,

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat k_d

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat k_i

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat k_po

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat k_pu

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat offset

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat passive

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat policy

step_wise

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat slope

0

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat temp ***************

45000

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat type

virtual_thermal

hjj@hjj-laptop:/sys/devices/virtual/thermal/thermal_zone4$ cat uevent

其中temp文件暴露温度给用户态读取. 它会调用到我们驱动中的代码

其它文件不用关心,它是系统自动生成的.

static int virt_get_temp(struct thermal_zone_device *tz, int *temp)

{

*temp = 45000; // 45℃

return 0;

}

//用户态接口通过系统调用到驱动的回调函数, 要求设备提供温度. 设备把测量的温度反馈回去.

为什么设备会挂靠在devices/virtual/ 总线下?

这是thermal_zone_device_register 函数调用决定的.

thermal 子系统提前提前注册了一个虚拟 class:

static struct class thermal_class = {

.name = "thermal",

.dev_groups = thermal_groups,

};

thermal_class 本身的属性,决定了设备会自动挂靠到 virtual 总线下

结果,最终自动生成路径:/sys/devices/virtual/thermal/thermal_zoneX

5. 小结:

本来,读取温度,在简单的嵌入式系统中, 只要从一个io地址读取数据就够了.

可是到了linux世界里却变得如此复杂.

主要是因为linux 内核管理之下,用户态程序不能访问I/O.

module 属于内核态, module 可以直接访问I/O,

但当温度由一个外部设备来提供时. 而且外部设备可能千奇百怪.

此时,内核要把外部测温设备想象成一个虚拟设备.

外部设备的数据都要符合一定的规范, 这样不管是任何外部测温设备,都能够和系统接上口.

其中thermal_tone 规范, 就是对外部设备的数据要求规范.

这样,系统屏蔽了具体的测温设备的差异, 具体的测温设备,靠它们的驱动绑定到系统上.

6. 测试代码

cpp 复制代码
$ cat virtual_thermal.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/err.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual Thermal Driver (8 params)");
MODULE_AUTHOR("test");

static struct thermal_zone_device *virt_tz;

// 温度获取函数(必须实现)
static int virt_get_temp(struct thermal_zone_device *tz, int *temp)
{
    *temp = 45000; // 45℃
    return 0;
}

// 操作函数集
static struct thermal_zone_device_ops virt_ops = {
    .get_temp = virt_get_temp,
};

// 空参数结构体
static struct thermal_zone_params virt_tzp = { };

// 探针函数
static int virt_probe(struct platform_device *pdev)
{
    pr_info("virt_thermal: probe\n");

    // ======================
    // 8 个参数 完全匹配5.4.156内核
    // ======================
    virt_tz = thermal_zone_device_register(  //注册一个thremal_zone,热区,温度区域
        "virtual_thermal",    // 1 type
        0,                    // 2 trips
        0,                    // 3 mask
        NULL,                 // 4 devdata
        &virt_ops,            // 5 ops
        &virt_tzp,            // 6 tzp
        0,                    // 7 passive_delay
        1000                  // 8 polling_delay (1秒)
    );

    if (IS_ERR(virt_tz)) {
        pr_err("thermal register failed\n");
        return PTR_ERR(virt_tz);
    }

    pr_info("virt_thermal: register ok\n");
    return 0;
}

static int virt_remove(struct platform_device *pdev)
{
    thermal_zone_device_unregister(virt_tz);
    pr_info("virt_thermal: removed\n");
    return 0;
}

static struct platform_driver virt_driver = {
    .probe = virt_probe,
    .remove = virt_remove,
    .driver = {
        .name = "virtual-thermal",
    },
};

static struct platform_device *virt_dev;

static int __init virt_init(void)
{
    virt_dev = platform_device_alloc("virtual-thermal", -1);
    platform_device_add(virt_dev); //添加了一个platform device, 名字叫virtual-thermal
    return platform_driver_register(&virt_driver); //注册了一个platform driver,叫virtual-thermal 与device 匹配

}

static void __exit virt_exit(void)
{
    platform_driver_unregister(&virt_driver);
    platform_device_unregister(virt_dev);
}

module_init(virt_init);
module_exit(virt_exit);

能不能再简化一下,不用platform总线,platform 设备, 直接注册一个thermal_zone 设备呢?

应该可以,去掉外边的这层包装就可以了. 代码还可以再继续缩小,

由此代码使我认识到, 系统提供了接口,驱动提供了实现,

这种接口和实现分离的原则.使得可以支持各种设备.

因为实现时驱动完成的,不同的设备跟不同的驱动就可以了.但都要符合内核规范.

7. 简化后的测试代码:

cpp 复制代码
$ cat virtual_thermal.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <linux/err.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtual Thermal Driver (8 params)");
MODULE_AUTHOR("test");

static struct thermal_zone_device *virt_tz;

// 温度获取函数(必须实现)
static int virt_get_temp(struct thermal_zone_device *tz, int *temp)
{
    *temp = 45000; // 45℃
    return 0;
}

// 操作函数集
static struct thermal_zone_device_ops virt_ops = {
    .get_temp = virt_get_temp,
};

// 空参数结构体
static struct thermal_zone_params virt_tzp = { };

static int __init virt_init(void)
{
    pr_info("virt_thermal: init\n");
    // ======================
    // 8 个参数 完全匹配5.4.156内核
    // ======================
    virt_tz = thermal_zone_device_register(  //注册一个thremal_zone,热区,温度区域
        "virtual_thermal",    // 1 type
        0,                    // 2 trips
        0,                    // 3 mask
        NULL,                 // 4 devdata
        &virt_ops,            // 5 ops        //这个热区实例,与驱动的virt_ops 相binding
        &virt_tzp,            // 6 tzp
        0,                    // 7 passive_delay
        1000                  // 8 polling_delay (1秒)
    );

    if (IS_ERR(virt_tz)) {
        pr_err("thermal register failed\n");
        return PTR_ERR(virt_tz);
    }

    pr_info("virt_thermal: register ok\n");
    return 0;
}

static void __exit virt_exit(void)
{
    thermal_zone_device_unregister(virt_tz);
    pr_info("virt_thermal: removed\n");
}

module_init(virt_init);
module_exit(virt_exit);

思考:

什么是linux 驱动?

Linux 驱动 = 给内核子系统注册一个设备 + 实现这个设备的操作方法

内核里多了一个设备,内核通过驱动来与设备实例交互.

其中platform设备与platform驱动有一个binding 过程.

相关推荐
hjjdebug20 天前
linux 如何读取的cpu 温度? (真实平台)
linux·driver·thermal_zone
hh.h.21 天前
昇腾 CANN driver 层架构:软硬件接口的深度解析
架构·昇腾·driver·cann
a7769957992 个月前
驱动里的并发控制--互斥锁
并发·driver
qq_283720052 个月前
Python 模块精讲:platform 获取系统信息,从入门到实战全攻略
python·platform
行者..................3 个月前
第2课:恢复出厂、掌握 Linux 基础命令并完成首次 GCC 编译
linux·qt·driver
大熊猫侯佩8 个月前
思过崖上学「 subprocess 」:令狐冲的 Swift 6.2 跨平台进程心法
spm·xcode·进程控制·platform·subprocess·output·swift 6.2
UWA1 年前
URP相机如何将场景渲染定帧模糊绘制
memory·platform·rendering
qq_282195311 年前
内核spi驱动流程图
linux·流程图·driver
UWA1 年前
如何用GPU Instancing来优化树木草石重复模型
ui·platform·rendering