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 过程.