linux系统中低精度定时器和高精度定时器的区别和使用方法

[TOC]

❓ 问题描述:

linux系统中低精度定时器和高精度定时器的区别和使用方法

日志

分析步骤

核心区别

linux系统中低精度定时器和高精度定时器的区别和使用方法

硬件依赖差异

1. 低精度定时器(基于 jiffies)
工作原理:
系统定时器会按固定频率(由内核配置的 HZ 决定,如 100Hz  1000Hz)产生 “滴答中断”(Timer Interrupt)。每发生一次中断,jiffies 计数器加 1,低精度定时器通过检查 jiffies 是否达到预设值来触发。
例如:若 HZ=100,则 1 个滴答 = 10ms,定时器的最小精度就是 10ms,无法设置比 10ms 更短的间隔。

2. 高精度定时器(hrtimer)
依赖的硬件:高精度时钟源, ARM 架构的 通用定时器(Generic Timer)。
工作原理:
高精度时钟源支持纳秒级时间戳和可编程中断,内核可直接通过硬件寄存器设置准确的触发时间。当硬件时钟达到预设时间时,会直接触发中断,调用定时器回调函数,无需等待系统滴答中断,因此精度极高。

代码片段

/*
 * @Author: your name
 * @Date: 2025-09-01 17:25:32
 * @LastEditTime: 2025-09-01 17:41:10
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: linux-4.14.143driversgpudrm	imer	imer.c
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
/*
struct timer_list {

        struct hlist_node       entry;
        unsigned long           expires;
        void                    (*function)(unsigned long);
        unsigned long           data;
        u32                     flags;

#ifdef CONFIG_LOCKDEP
        struct lockdep_map      lockdep_map;
#endif
};

使用普通定时器的步骤:
    1、定义定时器结构体
    2、初始化定时器
    3、设置定时器回调函数
    4、设置定时器触发时间
    5、启动定时器
    6、删除定时器(可选)
*/
// 普通定时器结构体
static struct timer_list normal_timer;
// 记录上一次触发时间(用于计算实际间隔)
static unsigned long last_jiffies;
// 触发次数计数器
static int trigger_count = 0;

// 定时器回调函数(中断上下文,不能睡眠)
static void normal_timer_handler(struct timer_list *t)
{
    unsigned long current_jiffies = jiffies;
    unsigned long interval_ms;

    // 计算实际触发间隔(转换为毫秒)
    interval_ms = jiffies_to_msecs(current_jiffies - last_jiffies);
    last_jiffies = current_jiffies;

    // 打印信息(包含当前计数和实际间隔)
    trigger_count++;
    printk(KERN_INFO "[普通定时器 = 0x%p] 第%d次触发,实际间隔: %lu ms (CPU: %d)
",
           t, trigger_count, interval_ms, smp_processor_id());

    // 重新设置定时器(100ms后再次触发)
    mod_timer(t, jiffies + msecs_to_jiffies(100));
}

// 驱动初始化函数
static int __init normal_timer_init(void)
{
    printk(KERN_INFO "普通定时器驱动初始化...
");

    // 初始化定时器
    timer_setup(&normal_timer, normal_timer_handler, 0);

    // 记录初始时间,首次触发延迟100ms
    last_jiffies = jiffies;
    mod_timer(&normal_timer, jiffies + msecs_to_jiffies(100));

    printk(KERN_INFO "普通定时器启动成功(预期间隔100ms,normal_timer = 0x%p)
", &normal_timer);

    return 0;
}

// 驱动卸载函数
static void __exit normal_timer_exit(void)
{
    // 删除定时器(确保不再触发)
    del_timer(&normal_timer);
    printk(KERN_INFO "普通定时器驱动卸载,共触发%d次
", trigger_count);
}

// 注册驱动入口和出口
module_init(normal_timer_init);
module_exit(normal_timer_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Normal Timer (jiffies-based) Demo");
MODULE_AUTHOR("Your Name");
/*
 * @Author: your name
 * @Date: 2025-09-01 17:49:16
 * @LastEditTime: 2025-09-01 18:07:28
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: linux-4.14.143driversgpudrm	imerhrtimer.c
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

/*
    高精度定时器使用步骤
    1. 定义高精度定时器结构体
    2. 初始化高精度定时器
    3. 绑定回调函数
    4. 启动高精度定时器
    5. 撤销高精度定时器


static inline ktime_t ktime_set(const long secs, const unsigned long nsecs);
    参数详解
    第一个参数 secs(const long 类型)
    表明时间的秒数部分(可以是正数或 0)。
    例如:secs = 1 表明 1 秒。
    若只需表明毫秒 / 微秒 / 纳秒级时间(无需整秒),可设为 0。
    第二个参数 nsecs(const unsigned long 类型)
    表明时间的纳秒数部分(范围:0 ~ 999,999,999)。
    1 秒 = 1,000,000,000 纳秒(1e9 纳秒),因此该参数不能超过 999,999,999。
    常见单位转换:
    1 毫秒(ms)= 1,000,000 纳秒(1e6 ns)
    1 微秒(us)= 1,000 纳秒(1e3 ns)
    函数功能
    将 “秒数 + 纳秒数” 合并为一个 ktime_t 类型的时间值(内核中用于表明高精度时间的统一格式),用于高精度定时器的触发时间设置、时间差计算等场景。
*/
// 高精度定时器结构体
static struct hrtimer hr_timer;
// 记录上一次触发时间(用于计算实际间隔)
static ktime_t last_ktime;
// 触发次数计数器
static int trigger_count = 0;
// 预期间隔(10ms,即10,000,000纳秒)
#define INTERVAL_NS 1000000000ULL

// 定时器回调函数(返回值决定是否重复触发)
static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer)
{
    ktime_t current_ktime;
    s64 interval_ns;

    // 获取当前时间并计算实际间隔(纳秒)
    current_ktime = ktime_get();
    interval_ns = ktime_to_ns(ktime_sub(current_ktime, last_ktime));
    last_ktime = current_ktime;

    // 打印信息(包含当前计数和实际间隔)
    trigger_count++;
    printk(KERN_INFO "[高精度定时器] 第%d次触发,实际间隔: %lld ns (CPU: %d)
",
           trigger_count, interval_ns, smp_processor_id());

    // 设置下一次触发时间(绝对时间=当前时间+间隔)
    hrtimer_forward(timer, current_ktime, ktime_set(0, INTERVAL_NS));
    //或者使用下面额函数进行设置
    //hrtimer_forward_now(timer, ktime_set(0, INTERVAL_NS));

    // 返回RESTART表明重复触发
    return HRTIMER_RESTART;
}

// 驱动初始化函数
static int __init hrtimerinit(void)
{
    printk(KERN_INFO "高精度定时器驱动初始化...
");

    // 初始化高精度定时器(模式:相对时间,CLOCK_MONOTONIC时钟)
    hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    // 绑定回调函数
    hr_timer.function = hrtimer_handler;

    // 记录初始时间,首次触发延迟10ms
    last_ktime = ktime_get();
    hrtimer_start(&hr_timer, ktime_set(0, INTERVAL_NS), HRTIMER_MODE_REL);

    printk(KERN_INFO "高精度定时器启动成功(预期间隔10,000,000 ns)
");
    return 0;
}

// 驱动卸载函数
static void __exit hrtimerexit(void)
{
    // 撤销定时器(等待当前回调执行完毕)
    hrtimer_cancel(&hr_timer);
    printk(KERN_INFO "高精度定时器驱动卸载,共触发%d次
", trigger_count);
}

// 注册驱动入口和出口
module_init(hrtimerinit);
module_exit(hrtimerexit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("High-Resolution Timer (hrtimer) Demo");
MODULE_AUTHOR("Your Name");

图片

✅ 结论

输出结论

待查资料问题

  • ❓ 问题 1:?
  • ❓ 问题 2:?

参考链接

  • Linux驱动中断与时间篇——高精度定时器hrtimer
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容