开发平台
* 芯灵思 SinlinxA33 开发板
淘宝店铺: [ https://sinlinx.taobao.com/ ]()
嵌入式 Linux 开发板交流 641395230
Linux 内核定时器是内核用来控制在未来某个时间点 (基于 jiffies) 调度执行某个函数的一种机制, 其实现位于 和 kernel/timer.c 文件中.
内核定时器的数据结构
- struct timer_list {
- struct list_head entry; // 双向链表元素 list: 用来将多个定时器连接成一条双向循环队列.
- unsigned long expires; //expires 字段表示期望定时器执行的 jiffies 值, 到达该 jiffies 值时, 将调用 function 函数, 并传递 data 作为参数.
- void (*function)(unsigned long); // 指向一个可执行函数. 当定时器到期时, 内核就执行 function 所指定的函数. 而 data 域则被内核用作 function 函数的调用参数.
- unsigned long data;
- struct tvec_base *base;
- /* ... */
- }
时间比较操作
在定时器应用中经常需要比较两个时间值, 以确定 timer 是否超时, 所以 Linux 内核在 timer.h 头文件中定义了 4 个时间关系比较操作宏. 这里我们说时刻 a 在时刻 b 之后, 就意味着时间值 a≥b.Linux 强烈推荐用户使用它所定义的下列 4 个时间比较操作宏(include/Linux/timer.h):
- #define time_after(a,b) ((long)(b) - (long)(a) <0)
- #define time_before(a,b) time_after(b,a)
- #define time_after_eq(a,b) ((long)(a) - (long)(b)>= 0)
- #define time_before_eq(a,b) time_after_eq(b,a)
Linux 内核时间相关转换函数
1. unsigned long usecs_to_jiffies(const unsigned int u)
功能: 把微秒转换成时钟节拍
参数: u 时间微秒
返回: 对应的时钟节拍数量
2. unsigned long msecs_to_jiffies(const unsigned int m)
功能: 把毫秒转换成时钟节拍
参数: u 时间毫秒
返回: 对应的时钟节拍数量
示例: 要定时从现在开始, 3 毫秒执行一个函数
expires 设置为 jiffies+ msecs_to_jiffies(3)
Linux 内核定时器操作相关 API
静态定义结构体变量并且初始化(宏)
DEFINE_TIMER(_name, _function, _expires, _data)
功能: 定义一个名字为_name 的 struct timer_list 结构的变量, 并且初始化它的 function, expires, data 成员
定时器初始化(宏)
init_timer(timer)
功能: 只是对 struct timer_list 结构成员进行一些基础初始化操作, function, expires, data 成员还需要用户自
己填充.
3. 设置定时器(宏)
setup_timer(timer, fn, data)
功能: 设置定时器中的 function, data 和一些基础成员, expires 并没有初始化, 需要用户自己进行初始化
注册定时器到内核
void add_timer(struct timer_list timer)
功能: 向内核注册一个定时器, 注册后会马上开始计时.
从内核注销定时器
int del_timer(struct timer_list timer);
功能: 从内核定时链表上删除指定的定时器, 删除后就不会再执行绑定的函数
修改定时器定时时间值, 并且重新注册
int mod_timer(struct timer_list timer, unsigned long expire0.s);
功能: 修改定时器定时时间值, 并且重新注册, 不管这个定时的超时函数是否执行过. 执行完成后会马上启
动定时.
内核定时器编程步骤
Step1 定义 timer_list 结构变量
Step2 定义超时函数
Step3 对 timer_list 结构变量进行初始化
Step4 注册定时器, 启动定时
Step5 注销定时器
实验平台: 芯灵思 SinlinxA33 开发板
驱动代码:
- #include <Linux/module.h>
- #include <Linux/init.h>
- #include <Linux/timer.h>
- //Step1 timer_list 结构变量
- struct timer_list timer;
- //Step2 超时函数
- void timer_fun(long data)
- {
- printk("%s is call! data:%d\r\n",__FUNCTION__,data);//__FUNCTION__ 获取当前函数名
- mod_timer(&timer, jiffies + HZ*1); // 再次修改本定时器超时时间为当前时间后 1 秒
- }
- static int __init timer_init(void)
- {
- //Step3 对 timer_list 结构变量进行初始
- init_timer(&timer);
- setup_timer(&timer, timer_fun, 666);
- timer.expires = jiffies + HZ*2;
- //Step4 注册定时器, 启动定时
- add_timer(&timer);
- printk("Timer start!\r\n");
- return 0;
- }
- static void __exit timer_exit(void) //Module exit function specified by module_exit()
- {
- //Step5 注销定时器
- del_timer_sync(&timer);
- printk("Timer over!\r\n");
- }
- module_init(timer_init);
- module_exit(timer_exit);
- MODULE_LICENSE("GPL");
Makefile 代码:
- KERN_DIR = /work/lichee/Linux-3.4
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
- obj-m += timer_drv.o
最后使用 dmseg 命令查看, 可以看到每隔 1 秒打印一次
来源: http://www.bubuko.com/infodetail-2961346.html