前言
Linux内核提供了两种定时器:低精度定时器(timer)和高精度定时器(hrtimer)。hrtimer可以提供纳秒级别的定时精度,适用于对时间精度要求较高的场景,如PWM模拟、精确采样等。本文将详细介绍hrtimer的原理和使用方法。
一、hrtimer与timer对比
1.1 主要区别
| 特性 | timer | hrtimer |
|---|---|---|
| 精度 | 毫秒级(基于jiffies) | 纳秒级 |
| 实现 | 时间轮算法 | 红黑树算法 |
| 执行上下文 | 软中断 | 硬中断 |
| 适用场景 | 一般定时任务 | 高精度定时 |
1.2 时钟源类型
// kernel-5.10/include/uapi/linux/time.h
#define CLOCK_REALTIME 0 // 实时时钟,可被用户修改
#define CLOCK_MONOTONIC 1 // 单调时钟,系统启动后递增
#define CLOCK_PROCESS_CPUTIME_ID 2 // 进程CPU时间
#define CLOCK_THREAD_CPUTIME_ID 3 // 线程CPU时间
#define CLOCK_BOOTTIME 7 // 包含休眠时间的单调时钟
1.3 定时器模式
// kernel-5.10/include/linux/hrtimer.h
enum hrtimer_mode {
HRTIMER_MODE_ABS = 0x00, // 绝对时间
HRTIMER_MODE_REL = 0x01, // 相对时间
HRTIMER_MODE_PINNED = 0x02, // 绑定到特定CPU
HRTIMER_MODE_SOFT = 0x04, // 软中断上下文执行
HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,
HRTIMER_MODE_ABS_SOFT = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
HRTIMER_MODE_REL_SOFT = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,
};
二、hrtimer API详解
2.1 数据结构
// kernel-5.10/include/linux/hrtimer.h
struct hrtimer {
struct timerqueue_node node; // 红黑树节点
ktime_t _softexpires; // 到期时间
enum hrtimer_restart (*function)(struct hrtimer *); // 回调函数
struct hrtimer_clock_base *base; // 时钟基准
u8 state; // 状态
u8 is_rel; // 是否相对时间
u8 is_soft; // 是否软中断
};
2.2 初始化
/**
* hrtimer_init - 初始化高精度定时器
* @timer: 定时器结构体
* @which_clock: 时钟源类型
* @mode: 定时器模式
*/
void hrtimer_init(struct hrtimer *timer,
clockid_t which_clock,
enum hrtimer_mode mode);
2.3 设置时间
/**
* ktime_set - 设置时间值
* @secs: 秒
* @nsecs: 纳秒
* 返回: ktime_t类型的时间值
*/
ktime_t ktime_set(const s64 secs, const unsigned long nsecs);
// 常用时间宏
#define NSEC_PER_SEC 1000000000L // 1秒 = 10^9纳秒
#define NSEC_PER_MSEC 1000000L // 1毫秒 = 10^6纳秒
#define NSEC_PER_USEC 1000L // 1微秒 = 10^3纳秒
// 示例
ktime_t kt;
kt = ktime_set(0, 1000000); // 1ms
kt = ktime_set(0, 20000000); // 20ms
kt = ktime_set(1, 0); // 1s
kt = ktime_set(0, 500 * NSEC_PER_USEC); // 500us
2.4 启动定时器
/**
* hrtimer_start - 启动定时器
* @timer: 定时器结构体
* @tim: 到期时间
* @mode: 定时器模式
*/
void hrtimer_start(struct hrtimer *timer,
ktime_t tim,
const enum hrtimer_mode mode);
2.5 取消定时器
/**
* hrtimer_cancel - 取消定时器
* @timer: 定时器结构体
* 返回: 0表示定时器未激活,1表示定时器已取消
*/
int hrtimer_cancel(struct hrtimer *timer);
/**
* hrtimer_try_to_cancel - 尝试取消定时器
* 返回: -1表示回调正在执行,0表示未激活,1表示已取消
*/
int hrtimer_try_to_cancel(struct hrtimer *timer);
2.6 重新设置时间
/**
* hrtimer_forward_now - 从当前时间向前设置
* @timer: 定时器结构体
* @interval: 时间间隔
* 返回: 跳过的周期数
*/
u64 hrtimer_forward_now(struct hrtimer *timer, ktime_t interval);
2.7 回调函数返回值
enum hrtimer_restart {
HRTIMER_NORESTART, // 不重启定时器
HRTIMER_RESTART, // 重启定时器(需先调用hrtimer_forward_now)
};
三、hrtimer使用示例
3.1 基本使用模板
// hrtimer_demo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
struct hrtimer_demo {
struct hrtimer timer;
ktime_t period;
int count;
};
static struct hrtimer_demo demo_data;
static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
{
struct hrtimer_demo *demo = container_of(timer, struct hrtimer_demo, timer);
demo->count++;
pr_info("hrtimer callback: count=%d\n", demo->count);
// 重新设置下次触发时间
hrtimer_forward_now(timer, demo->period);
// 返回HRTIMER_RESTART继续定时
return HRTIMER_RESTART;
}
static int __init hrtimer_demo_init(void)
{
pr_info("hrtimer demo init\n");
// 设置定时周期:100ms
demo_data.period = ktime_set(0, 100 * NSEC_PER_MSEC);
demo_data.count = 0;
// 初始化定时器
hrtimer_init(&demo_data.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
demo_data.timer.function = hrtimer_callback;
// 启动定时器
hrtimer_start(&demo_data.timer, demo_data.period, HRTIMER_MODE_REL);
return 0;
}
static void __exit hrtimer_demo_exit(void)
{
hrtimer_cancel(&demo_data.timer);
pr_info("hrtimer demo exit, total count=%d\n", demo_data.count);
}
module_init(hrtimer_demo_init);
module_exit(hrtimer_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hrtimer demo driver");
3.2 Platform驱动集成
// hrtimer_platform.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/of.h>
struct hrtimer_priv {
struct device *dev;
struct hrtimer timer;
ktime_t period;
bool running;
};
static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer)
{
struct hrtimer_priv *priv = container_of(timer, struct hrtimer_priv, timer);
// 执行定时任务
dev_dbg(priv->dev, "timer triggered\n");
if (priv->running) {
hrtimer_forward_now(timer, priv->period);
return HRTIMER_RESTART;
}
return HRTIMER_NORESTART;
}
static ssize_t period_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hrtimer_priv *priv = dev_get_drvdata(dev);
return sprintf(buf, "%lld\n", ktime_to_ns(priv->period));
}
static ssize_t period_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hrtimer_priv *priv = dev_get_drvdata(dev);
u64 ns;
if (kstrtou64(buf, 10, &ns))
return -EINVAL;
priv->period = ns_to_ktime(ns);
return count;
}
static DEVICE_ATTR_RW(period);
static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct hrtimer_priv *priv = dev_get_drvdata(dev);
bool enable;
if (kstrtobool(buf, &enable))
return -EINVAL;
if (enable && !priv->running) {
priv->running = true;
hrtimer_start(&priv->timer, priv->period, HRTIMER_MODE_REL);
} else if (!enable && priv->running) {
priv->running = false;
hrtimer_cancel(&priv->timer);
}
return count;
}
static DEVICE_ATTR_WO(enable);
static struct attribute *hrtimer_attrs[] = {
&dev_attr_period.attr,
&dev_attr_enable.attr,
NULL,
};
ATTRIBUTE_GROUPS(hrtimer);
static int hrtimer_platform_probe(struct platform_device *pdev)
{
struct hrtimer_priv *priv;
u32 period_ms = 100; // 默认100ms
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = &pdev->dev;
// 从设备树读取周期
of_property_read_u32(pdev->dev.of_node, "period-ms", &period_ms);
priv->period = ktime_set(0, period_ms * NSEC_PER_MSEC);
// 初始化定时器
hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
priv->timer.function = hrtimer_handler;
platform_set_drvdata(pdev, priv);
// 创建sysfs属性
return devm_device_add_groups(&pdev->dev, hrtimer_groups);
}
static int hrtimer_platform_remove(struct platform_device *pdev)
{
struct hrtimer_priv *priv = platform_get_drvdata(pdev);
priv->running = false;
hrtimer_cancel(&priv->timer);
return 0;
}
static const struct of_device_id hrtimer_of_match[] = {
{ .compatible = "demo,hrtimer" },
{ }
};
MODULE_DEVICE_TABLE(of, hrtimer_of_match);
static struct platform_driver hrtimer_platform_driver = {
.probe = hrtimer_platform_probe,
.remove = hrtimer_platform_remove,
.driver = {
.name = "hrtimer-demo",
.of_match_table = hrtimer_of_match,
},
};
module_platform_driver(hrtimer_platform_driver);
MODULE_LICENSE("GPL");
设备树配置:
hrtimer_demo: hrtimer-demo {
compatible = "demo,hrtimer";
period-ms = <50>; // 50ms周期
status = "okay";
};
3.3 GPIO模拟PWM
// gpio_pwm.c - 使用hrtimer模拟PWM输出
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/hrtimer.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
struct gpio_pwm {
struct device *dev;
struct gpio_desc *gpio;
struct hrtimer timer;
ktime_t period;
ktime_t duty;
bool state;
bool enabled;
};
static enum hrtimer_restart gpio_pwm_handler(struct hrtimer *timer)
{
struct gpio_pwm *pwm = container_of(timer, struct gpio_pwm, timer);
ktime_t next;
if (!pwm->enabled)
return HRTIMER_NORESTART;
// 切换GPIO状态
pwm->state = !pwm->state;
gpiod_set_value(pwm->gpio, pwm->state);
// 计算下次触发时间
if (pwm->state) {
next = pwm->duty; // 高电平持续时间
} else {
next = ktime_sub(pwm->period, pwm->duty); // 低电平持续时间
}
hrtimer_forward_now(timer, next);
return HRTIMER_RESTART;
}
static ssize_t duty_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct gpio_pwm *pwm = dev_get_drvdata(dev);
u32 duty_ns;
if (kstrtou32(buf, 10, &duty_ns))
return -EINVAL;
pwm->duty = ns_to_ktime(duty_ns);
return count;
}
static DEVICE_ATTR_WO(duty);
static int gpio_pwm_probe(struct platform_device *pdev)
{
struct gpio_pwm *pwm;
u32 period_ns = 1000000; // 默认1ms周期 (1KHz)
u32 duty_ns = 500000; // 默认50%占空比
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm)
return -ENOMEM;
pwm->dev = &pdev->dev;
// 获取GPIO
pwm->gpio = devm_gpiod_get(&pdev->dev, "pwm", GPIOD_OUT_LOW);
if (IS_ERR(pwm->gpio))
return PTR_ERR(pwm->gpio);
// 读取参数
of_property_read_u32(pdev->dev.of_node, "period-ns", &period_ns);
of_property_read_u32(pdev->dev.of_node, "duty-ns", &duty_ns);
pwm->period = ns_to_ktime(period_ns);
pwm->duty = ns_to_ktime(duty_ns);
// 初始化定时器
hrtimer_init(&pwm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pwm->timer.function = gpio_pwm_handler;
// 启动PWM
pwm->enabled = true;
pwm->state = true;
gpiod_set_value(pwm->gpio, 1);
hrtimer_start(&pwm->timer, pwm->duty, HRTIMER_MODE_REL);
platform_set_drvdata(pdev, pwm);
dev_info(&pdev->dev, "GPIO PWM started: period=%uns, duty=%uns\n",
period_ns, duty_ns);
return 0;
}
static int gpio_pwm_remove(struct platform_device *pdev)
{
struct gpio_pwm *pwm = platform_get_drvdata(pdev);
pwm->enabled = false;
hrtimer_cancel(&pwm->timer);
gpiod_set_value(pwm->gpio, 0);
return 0;
}
static const struct of_device_id gpio_pwm_of_match[] = {
{ .compatible = "gpio-pwm" },
{ }
};
MODULE_DEVICE_TABLE(of, gpio_pwm_of_match);
static struct platform_driver gpio_pwm_driver = {
.probe = gpio_pwm_probe,
.remove = gpio_pwm_remove,
.driver = {
.name = "gpio-pwm",
.of_match_table = gpio_pwm_of_match,
},
};
module_platform_driver(gpio_pwm_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("GPIO PWM using hrtimer");
四、注意事项
4.1 回调函数执行时间
hrtimer回调在硬中断上下文执行,必须注意:
-
不能调用可能睡眠的函数
-
执行时间要尽量短
-
复杂任务应使用工作队列
// 错误示例 - 回调中睡眠
static enum hrtimer_restart bad_callback(struct hrtimer *timer)
{
msleep(10); // 错误!不能在中断上下文睡眠
return HRTIMER_NORESTART;
}// 正确示例 - 使用工作队列
static struct work_struct my_work;static void work_handler(struct work_struct *work)
{
// 可以执行耗时操作
msleep(10);
}static enum hrtimer_restart good_callback(struct hrtimer *timer)
{
schedule_work(&my_work); // 调度工作队列
return HRTIMER_NORESTART;
}
4.2 精度限制
虽然hrtimer支持纳秒级精度,但实际精度受限于:
-
硬件定时器精度
-
中断延迟
-
系统负载
// 测试实际精度
static ktime_t last_time;static enum hrtimer_restart precision_test(struct hrtimer *timer)
{
ktime_t now = ktime_get();
ktime_t diff = ktime_sub(now, last_time);pr_info("actual interval: %lld ns\n", ktime_to_ns(diff)); last_time = now; hrtimer_forward_now(timer, ns_to_ktime(1000000)); // 1ms return HRTIMER_RESTART;}
4.3 CPU绑定
对于时间敏感的应用,可以将定时器绑定到特定CPU:
// 绑定到当前CPU
hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
五、调试方法
5.1 查看定时器状态
# 查看hrtimer统计
cat /proc/timer_list | grep -A 20 "hrtimer"
# 查看时钟源
cat /sys/devices/system/clocksource/clocksource0/available_clocksource
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
5.2 ftrace跟踪
# 启用hrtimer跟踪
echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_start/enable
echo 1 > /sys/kernel/debug/tracing/events/timer/hrtimer_expire_entry/enable
# 查看跟踪结果
cat /sys/kernel/debug/tracing/trace
六、常见问题
6.1 定时器不触发
检查步骤:
- 确认定时器已启动
- 检查回调函数是否正确设置
- 检查时间参数是否合理
6.2 定时精度不够
可能原因:
- 系统负载过高
- 中断被禁用
- 回调执行时间过长
解决方法:
- 使用HRTIMER_MODE_PINNED绑定CPU
- 减少回调执行时间
- 使用实时内核(PREEMPT_RT)
七、总结
本文介绍了Linux高精度定时器hrtimer的使用:
- 基本概念:纳秒级精度,红黑树实现
- API使用:初始化、启动、取消、重设
- 实际应用:周期任务、GPIO PWM模拟
- 注意事项:中断上下文、精度限制
hrtimer适用于对时间精度要求较高的场景,但需要注意其在硬中断上下文执行的特性。