在嵌入式软件开发中,使用定时器的目的是为了实现周期性地执行某项工作;同样地,linux内核也实现了一种定时器,用于实现内核周期性执行某项工作。
1.基本概念
linux内核实现的这种定时器,称之为动态定时器,是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,它的实现位于Linux/timer.h 和 kernel/timer.c 文件中。
被调度的函数是异步执行的,类似于一种“软件中断”,处于非进程的上下文中,所以这个被调度函数应该遵循如下规则:
- 没有 current 指针,不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系;
- 不能执行休眠(或可能引起休眠的函数)和调度;
- 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件;
- 被调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行;
- 定时函数实现不能太耗时,耗时要尽量短。
2.数据结构
内核提供给驱动许多函数来声明, 注册, 以及删除内核定时器:
#include <linux/timer.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;
......
};
3.初始化
#define timer_setup(timer, callback, flags) \
__init_timer((timer), (callback), (flags))
需要注意的是kernel版本4.15之前初始化函数是init_timer,在4.15之后改为timer_setup。
4. 注册
初始化定时器成功之后,我们需要注册该定时器,函数为:
void add_timer(struct timer_list *timer);
5.修改定时时长
定时运行过程中的定时器周期修改函数为:
int mod_timer(struct timer_list *timer, unsigned long expires);
通常要实现循环定时功能一般是在定时函数结束的时候通过mod_timer重新设置定时时长来实现的:
void timer_fun(struct timer_list *timer)
{
// do some thing
mod_timer(timer, jiffies + HZ);
}
6.删除定时器
当不需要定时器时通过如下函数删除定时器:
int del_timer(struct timer_list * timer);
7.测试
完整的测试代码如下:
https://github.com/zhaoxd298/Linux_drivers/tree/master/timer