VERSION:5.4.24
参考:
include/linux/timer.h
kernel/time/timer.c
include/linux/jiffies.h
重要的结构体,函数
-
定时器结构体
struct timer_list { /* * All fields that change during normal runtime grouped to the * same cacheline */ struct hlist_node entry; unsigned long expires; void (*function)(struct timer_list *); u32 flags; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; /* * 只关注两个成员 * expires : 定时器超时时间 * function: 定时器超时的执行函数 */
-
定时器初始化
-
方法一
/* * @_name: 定义的timer_list名字 * @_function: 定时器超时的执行函数 */ #define DEFINE_TIMER(_name, _function) \ struct timer_list _name = \ __TIMER_INITIALIZER(_function, 0)
-
方法二
//include/linux/timer.h /* * @timer: 传入的timer_list指针 * @callback: 定时器超时的执行函数 * @flags: 标志位,默认为0即可 */ #define timer_setup(timer, callback, flags) \ __init_timer((timer), (callback), (flags))
-
-
启动定时器
/** * add_timer - 启动一个计时器 * @timer:要添加的定时器 * * 将来,内核将从-> expires点的计时器中断中执行-> function(@timer)回调。 当前时间是“jiffies”。 * * 定时器的 ->expires, ->function 字段必须在调用此函数之前设置。 * * 过去带有 ->expires 字段的计时器将在下一个计时器滴答中执行。 */ void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); mod_timer(timer, timer->expires); } EXPORT_SYMBOL(add_timer);
-
修改定时器超时时间
/** * mod_timer - 修改定时器的超时时间 * @timer:要修改的定时器 * @expires: jiffies 中的新超时 * * mod_timer() 是更新活动计时器的过期字段的更有效方法(如果计时器处于非活动状态,它将被激活) * * mod_timer(timer, expires) 等价于:del_timer(定时器); 计时器->过期=过期; 添加计时器(计时器); * * 请注意,如果同一计时器有多个未序列化的并发用户,那么 mod_timer() 是修改超时的唯一安全方法,因为 add_timer() 无法修改已运行的计时器。 * * 该函数返回是否修改了一个挂起的计时器。 *(即,非活动定时器的 mod_timer() 返回 0,活动定时器的 mod_timer() 返回 1。) */ int mod_timer(struct timer_list *timer, unsigned long expires) { return __mod_timer(timer, expires, 0); } EXPORT_SYMBOL(mod_timer);
-
删除定时器
/** * del_timer - 停用计时器。 * @timer:要停用的计时器 * * del_timer() 停用计时器 - 这适用于活动和非活动计时器。 * * 该函数返回是否取消了待处理的计时器。 *(即,非活动定时器的 del_timer() 返回 0,活动定时器的 del_timer() 返回 1。) */ int del_timer(struct timer_list *timer) { struct timer_base *base; unsigned long flags; int ret = 0; debug_assert_init(timer); if (timer_pending(timer)) { base = lock_timer_base(timer, &flags); ret = detach_if_pending(timer, base, true); raw_spin_unlock_irqrestore(&base->lock, flags); } return ret; } EXPORT_SYMBOL(del_timer);
-
时间转换
/** * msecs_to_jiffies: - 将毫秒转换为 jiffies * @m: 以毫秒为单位的时间 * * 转换如下: * * 负值意味着“无限超时”(MAX_JIFFY_OFFSET) * * “太大”值[会导致大于 MAX_JIFFY_OFFSET 值]也意味着“无限超时”。 * */ static __always_inline unsigned long msecs_to_jiffies(const unsigned int m) { if (__builtin_constant_p(m)) { if ((int)m < 0) return MAX_JIFFY_OFFSET; return _msecs_to_jiffies(m); } else { return __msecs_to_jiffies(m); } }
内核定时器使用示例一
-
第一步:构造结构体
/* 定义结构体 */ static struct timer_list my_timer; /* 实现超时函数 */ void timer_work(struct timer_list * ptimer) { /* user code */ }
-
第二步:初始化定时器
/* * 在合适的位置添加这一句代码来初始化定时器 * 一般在驱动的 probe,open函数里使用 */ timer_setup(&my_timer, timer_work, 0);
-
第三步:启动定时器
/* 在合适的位置添加这一句代码来启动定时器 */ mod_timer(&my_timer, jiffies + msecs_to_jiffies(500)); // 500ms 后执行超时函数
内核定时器使用示例二
-
第一步:构造结构体
/* 实现超时函数 */ void timer_work(struct timer_list * ptimer) { /* user code */ }
-
第二步:初始化定时器
/* * 在函数体外面使用,可以加static关键字 */ DEFINE_TIMER(my_timer, timer_work);
-
第三步:启动定时器
/* 在合适的位置添加这一句代码来启动定时器 */ mod_timer(&my_timer, jiffies + msecs_to_jiffies(500)); // 500ms 后执行超时函数
注意事项
- timer_list 是中断上下文,要遵守中断处理原则:不能嵌套。
所以 i2c_transfer 之类涉及中断的函数不能在超时函数里使用。否则会导致内核奔溃。