linux内核定时器如何使用?
内核定时器不是周期性的, 一次定时时间到了以后就会关闭, 除非重新打开
关键函数
头文件 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)(unsigned long);//定时器超时函数
unsigned long data;//传入超时函数的参数
u32 flags;
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
通过宏 from_timer 可以获取包含定时器结构体的首地址
①setup_timer(timer, fn, data);
设置定时器,主要是初始化timer_list结构体,设置其中的函数、参数。
*② void add_timer(struct timer_list timer):
向内核添加定时器。timer->expires表示超时时间。
当超时时间到达,内核就会调用这个函数:timer->function(timer->data)。
*③ int mod_timer(struct timer_list timer, unsigned long expires):
修改定时器的超时时间,
它等同于:del_timer(timer); timer->expires = expires; add_timer(timer);
但是更加高效。
*④ int del_timer(struct timer_list timer):
删除定时器。
**⑤int del_timer_sync(struct timer_list *timer) **
del_timer_sync函数是 del_timer函数的同步版,会等待其他处理器使用完定时器再删除,
del_timer_sync不能使用在中断上下文中。
int del_timer_sync(struct timer_list *timer)
定时器时间单位
编译内核时,可以在内核源码根目录下用“ls -a”看到一个隐藏文件 .config, 打开后可以看到如下这项:
CONFIG_HZ=100
这表示内核每秒中会发生100次系统滴答中断
每发生一次tick中断,全局变量jiffies就会累加1。
这个在内核配置是可以修改的
menuconfig的
-> Kernel Features
-> Timer frequency (<choice> [=y])
全局变量 jiffies
既然是变量那么就有溢出的风险, 内核提供了宏来避免溢出造成的错误
time_after(unkown, known) unkown > known?
time_before(unkown, known) unkown < known?
time_after_eq(unkown, known) unkown >= known?
time_before_eq(unkown, known) unkown <= known?
unkown通常为 jiffies,known通常是需要对比的值。
Linux内核提供了几个 jiffies和 ms、us、ns之间的转换函数
//将 jiffies类型的参数 j分别转换为对应的毫秒、微秒、纳秒。
int jiffies_to_msecs(const unsigned long j)
int jiffies_to_usecs(const unsigned long j)
u64 jiffies_to_nsecs(const unsigned long j)
// 将毫秒、微秒、纳秒转换为 jiffies类型。
long msecs_to_jiffies(const unsigned int m)
long usecs_to_jiffies(const unsigned int u)
unsigned long nsecs_to_jiffies(u64 n)
unsigned long timeout;
timeout = jiffies + (2 * HZ); /* 超时的时间点 */
/*************************************
具体的代码
************************************/
/* 判断有没有超时 */
if(time_before(jiffies, timeout)) {
/* 超时未发生 */
} else {
/* 超时发生 */
}
定时器的时间就是基于jiffies的,我们修改超时时间时,一般使用这2种方法:
① 在add_timer之前,直接修改:
timer.expires = jiffies + xxx; // xxx表示多少个滴答后超时,也就是xxx10ms
timer.expires = jiffies + 2HZ; // HZ等于CONFIG_HZ,2HZ就相当于2秒
**!宏 HZ 表示一秒的节拍数, 即频率 **
② 在add_timer之后,使用mod_timer修改:
mod_timer(&timer, jiffies + xxx); // xxx表示多少个滴答后超时,也就是xxx10ms
mod_timer(&timer, jiffies + 2HZ); // HZ等于CONFIG_HZ,2HZ就相当于2秒
编码
核心代码:
定时器超时函数申明:
static void key_timer_expire(unsigned long data)
{
/* data ==> gpio */
struct gpio_dev *dev = data;//地址强转
}
初始化定时器:
struct gpio_dev{
int gpio;
......
struct timer_list key_timer;//声明一个定时器
} ;
static struct gpio_dev dev;
//初始化定时器
setup_timer(&dev.key_timer, key_timer_expire, &dev);
向内核添加定时器:
注意: 这里add_timer添加之后 如果超时时间是0, 那么超时函数会被马上调用, 所以在刚开始时,要先设成最大值
dev.key_timer.expires = ~0; //~0 变成最大值
add_timer(&gpio_keys_100ask[i].key_timer);
设置定时器超时时间:
如果定时器还没有激活的话,mod_timer函数会激活定时
器
//HZ表示1000毫秒, HZ/50表示20毫秒
mod_timer(&dev->key_timer, jiffies + HZ/50);//也可以使用宏jffies + msecs_to_jiffies(20);
内核短延迟函数
纳秒、微秒和毫秒延时函数。
void ndelay(unsigned long nsecs)
void udelay(unsigned long usecs)
void mdelay(unsigned long mseces)