自学内容网 自学内容网

linux内核定时器

一、jiffies定时器

传统定时器(基于jiffies)是Linux内核中最早和最基本的定时机制。jiffies是一个全局变量,它记录了自系统启动以来的“滴答数”(ticks)。每次时钟中断发生时,jiffies会增加。这个机制基于系统时钟中断的频率,因此其精度受到时钟频率的限制。

1.1 工作原理

  1. 时钟中断:
    时钟中断是一个固定频率的周期性中断事件,每次时钟中断发生时,内核会增加jiffies的值;
    时钟频率通常由宏HZ定义,比如HZ=1000表示每秒发生1000次时钟中断(每毫秒一次);
  2. jiffies变量:
    jiffies是一个全局变量,用于记录自系统启动以来的时钟中断次数;
    在每次时钟中断处理过程中,内核会增加jiffies的值;

1.2 timer_list结构体

传统定时器使用struct timer_list结构体来定义和管理定时器任务。

struct timer_list {
    struct hlist_node entry;
    unsigned long expires; // 定时器到期的jiffies值
    void (*function)(unsigned long); // 定时器回调函数
    unsigned long data; // 回调函数参数
};

1.3 相关接口

定时器相关接口定义在kernel\time\timer.c文件中。主要流程涉及以下接口。

1.3.1 初始化和启动定时器

使用add_timer接口启动定时器,参数是timer_list

void add_timer(struct timer_list *timer)

代码示例:

#include <linux/timer.h>
#include <linux/jiffies.h>

static struct timer_list my_timer;

void my_timer_callback(unsigned long data)
{
    printk(KERN_INFO "Timer callback function called with data: %lu\n", data);
}

void setup_timer(void)
{
    // 初始化定时器
    init_timer(&my_timer);
    my_timer.function = my_timer_callback;
    my_timer.data = 0; // 回调函数参数
    my_timer.expires = jiffies + HZ; // 1秒后到期

    // 添加定时器到内核
    add_timer(&my_timer);
}

类似的接口还有add_timer_on,该接口功能是在指定的cpu上启动一个定时器:

void add_timer_on(struct timer_list *timer, int cpu)

1.3.2 修改定时器

使用mod_timer接口修改定时器的超时时间:

void my_timer_callback(unsigned long data)
{
    printk(KERN_INFO "Timer callback function called with data: %lu\n", data);
    // 如果需要周期性定时器, 使用mod_timer修改定时器超时时间
mod_timer(&my_timer, jiffies + msecs_to_jiffies(500));
}

在上述定时器回调接口my_timer_callback中,调用mod_timer修改定时器的超时时间为500ms,这样能给实现每隔500ms循环调用定时器超时回调接口。
mod_timer接口的功能相当于del_timer(timer); timer->expires = expires; add_timer(timer);,即删除定时器、设置定时器超时时间、启动定时器。

1.3.3 删除定时器

使用del_timer接口删除定时器:

del_timer(&my_timer);

1.3.4 jiffies相关接口

上述定时器单位是以jiffies为单位的,添加定时器和修改定时器的超时时间,都是以jiffies为单位的。下面介绍几个常用的jiffies与其它时间单位转化的接口:

static __always_inline unsigned long msecs_to_jiffies(const unsigned int m);
unsigned int jiffies_to_msecs(const unsigned long j);
static __always_inline unsigned long usecs_to_jiffies(const unsigned int u);
unsigned int jiffies_to_usecs(const unsigned long j);

static inline unsigned long timespec_to_jiffies(const struct timespec *value);
static inline void jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);

unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value);

二、高精度定时器

传统定时器的精度和实时性受限于系统的时钟中断频率和调度机制;
相对于传统定时器(jiffies),高精度定时器可以提供纳秒级的定时精度,适用于实时应用、精确计时等需求;

2.1 hrtimer结构体

struct hrtimer {
    struct timerqueue_node node; // 定时器队列节点
    ktime_t _softexpires;        // 定时器到期时间
    enum hrtimer_restart (*function)(struct hrtimer *); // 回调函数
    enum hrtimer_mode state;     // 定时器状态
    clockid_t clock_id;          // 使用的时钟类型
};

2.2 相关接口

2.2.1 初始化和启动定时器

linux中,可以使用hrtimer_init接口初始化一个高精度定时器;
使用hrtimer_start接口启动定时器;
示例代码:

#include <linux/hrtimer.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/init.h>

static struct hrtimer my_hrtimer;

enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    printk(KERN_INFO "High-Resolution Timer Callback\n");
    // 如果需要周期性定时器,重新启动定时器
    hrtimer_forward_now(timer, ns_to_ktime(1000000000)); // 1秒
    return HRTIMER_RESTART;
}

void setup_hrtimer(void)
{
    ktime_t ktime;
    // 初始化高精度定时器
    hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    my_hrtimer.function = &my_timer_callback;

    // 设置定时器到期时间
    ktime = ktime_set(0, 1000000000); // 1秒
    hrtimer_start(&my_hrtimer, ktime, HRTIMER_MODE_REL);
}

在示例代码中,使用hrtimer_init初始化一个定时器,使用hrtimer_start接口设置定时器超时时间是 1s 并启动定时器。

2.2.2 取消定时器

当模块卸载或者不需要定时器时,使用hrtimer_cancel接口取消定时器:

ret = hrtimer_cancel(&my_hrtimer);

2.2.3 通过定时器实现周期性任务

在定时器回调函数接口中,使用hrtimer_start 重启启动定时器,或者使用hrtimer_forward_now 接口更新定时器的到期时间,都能够实现周期性任务。

hrtimer_forward_now:
将定时器的到期时间向前推进一个时间间隔;
适合保持固定周期,即使回调函数的执行时间可能有波动。
hrtimer_start:
重新启动定时器,可以灵活地设置新的到期时间;
适用于需要在每次回调函数中动态调整定时器时间的场景。

代码示例:

enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    printk(KERN_INFO "High-Resolution Timer Callback\n");
    // 如果需要周期性定时器,重新启动定时器
    // 方法一:
    hrtimer_forward_now(timer, ns_to_ktime(1000000000)); // 1秒
    // 方法二:
    hrtimer_start(&my_hrtimer, ktime, HRTIMER_MODE_REL);
    return HRTIMER_RESTART;
}

注意:

  • 虽然 hrtimer 提供纳秒级的精度,但是实际精度受硬件和系统调度的影响,会导致定时器实际触发的时间可能会有稍微的偏差;
  • 这就导致使用hrtimer_start接口实现周期性任务时,每次产生一些偏差,可能会产生累计误差;
  • hrtimer_forward_now接口是在定时器当前这次理论到期时间基础上,向前推进一个周期设置下次超时时间,所以不会有累积误差。如果对周期性的时间准确性有要求,建议使用hrtimer_forward_now接口。

原文地址:https://blog.csdn.net/qq_28499879/article/details/140248594

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!