1、Linux内核定时器timer_list
内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制
2、timer_list 4.15之前的版本
void callback(unsigned long arg)
struct timer_list {
/*
* A1l fields that change during normal runtime grouped to the
* same cacheline
*/
struct list_head entry;/*定时器列表*/
unsigned long expires;/* 定时器到期时间*/
struct tvec_base *base;
void (*function) (unsigned long);/*定时器处理函数*/
unsigned 1ong data;
};
3、定时器操作 4.15之前
●初始化
struct timer_list mytimer ;
void init_timer(struct timer_list *timer);|
●增加定时器
void
add_timer(struct timer_list *timer)
●修改定时器的expire
int mod_timer(struct timer_list *timer, unsigned 1ong expires)
●删除定时器
int del_timer(struct timer_list *timer)
4、内核定时器执行过程
内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销) , 但可以通过在被调度的函数中重新调度自己来周期运行。
在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。
5、使用实例 4.15版本之前
void callback(unsigned long arg)
{
add_timer(&tm) ; //重新开始计时
}
init_timer(&tm) ; //初始化内核定时器
tm.function = callback; //指定定时时间到后的回调函数
tm.data = (unsigned 1ong)"hello world" ;//回调函数的参数
tm.expires = jiffies+1*HZ; //定时时间
add_timer(&tm); //注册定时器
6、Jiffies
●jiffies是记录着从电脑开机到现在总共的时钟中断次数
●5s后jiffies + 5*Hz
●可以计算一下:
32位: 497天后溢出
64位:天文数字
7、其他函数do_gettimeofday
●在Linux中可以使用函数do_ gettimeofday()函数来得到精确时间。它的精度可以达到微妙,是与C标准库中
gettimeofday()用法相同的函数。
●do_ gettimeofday)会把目前的时间用tv结构体返回,当地时区的信息则放到tV所指的结构中。
struct timeval {
time_t tV_sec; /* seconds */
suseconds_t tV_usec; /* microseconds */
};
8、实例代码 4.15版本之前
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>
static struct timer_list tm;
struct timeval oldtv;
void callback(unsigned long arg)
{
struct timeval tv;
char *strp = (char*)arg;
printk("%s: %lu, %s\n", __func__, jiffies, strp);
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //与上次中断间隔 s
tv.tv_usec- oldtv.tv_usec); //与上次中断间隔 ms
oldtv = tv;
tm.expires = jiffies+1*HZ;
add_timer(&tm); //重新开始计时
}
static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
init_timer(&tm); //内核初始化定时器
do_gettimeofday(&oldtv); //获取当前时间
tm.function= callback; //指定定时时间到后的回调函数
tm.data = (unsigned long)"hello world"; //回调函数的参数
tm.expires = jiffies+1*HZ; //定时时间
add_timer(&tm); //注册定时器
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //注销定时器
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yikoupeng");
MODULE_DESCRIPTION("timerlist");
9、效果 4.15版本之前
10 、timer_list 4.15之后的版本
初始化:
初始化从init_timer()
变成了timer_setup()
11、示例代码 4.15版本之后
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <linux/timer.h>
static struct timer_list tm;
struct timeval oldtv;
void callback(struct timer_list* timer)
{
struct timeval tv;
do_gettimeofday(&tv);
printk("%s: %ld, %ld\n", __func__,
tv.tv_sec - oldtv.tv_sec, //与上次中断间隔 s
tv.tv_usec- oldtv.tv_usec); //与上次中断间隔 ms
oldtv = tv;
tm.expires = jiffies + 2*HZ;
add_timer(&tm); //重新开始计时
}
static int __init demo_init(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
timer_setup(&tm, callback, 0);
do_gettimeofday(&oldtv); //获取当前时间
tm.function= callback; //指定定时时间到后的回调函数
add_timer(&tm); //注册定时器
return 0;
}
static void __exit demo_exit(void)
{
printk(KERN_INFO "%s : %s : %d - ok.\n", __FILE__, __func__, __LINE__);
del_timer(&tm); //注销定时器
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yikoupeng");
MODULE_DESCRIPTION("timerlist");
12、效果
13、内核定时器使用注意事项
●被调度的函数肯定是异步执行的,它类似于一种“软件中断”, 而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:
●没有current指针、不允许访问用户空间【因为没有进程上下文,相关代码和被中断的进程没有任何联系】
●不能执行休眠(或可能弓|起休眠的函数)和调度。
●任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
14、其他时间函数
举例
time_before:
●0.5秒后超时
●unsigned long timeout = jiffies + HZ/2;
●//注意jiffies值溢出回绕用宏time_ before 而非直timeout > jffries
if(time_before(jiffies ,timeout)){
//没有超时
}else{
//超时
}