|
Linux内核定时器的使用
linux内核中定时器的使用,定时器是很重要的内容,在调试TP或者其他许多程序时都涉及到定时器的使用,因此掌握定时器的运用是必备的。
下面将介绍定时器驱动的常用函数。对于具体的驱动后面的文档会以蜂鸣器驱动为例,并介绍框架层及应用怎样去控制蜂鸣器。
1.linux系统时间频率定义
系统定时器的时钟频率HZ 定义在 arch/arm/include/asm/param.h
#define Hz 100 //ARM构架基本都是100
2.节拍总数(jiffies)
全局变量jiffies用来记录自系统启动以来产生的节拍总数,根据这个节拍总数可以获得系统自启动以来的时间,linux系统启动时,会将jiffies初始化为0,
3.访问jiffies变量
jiffies总是无符号长整数,该变量定义在linux/jiffies.h文件中
内核定时器
使用内核定时器的步骤
1. 定义内核定时器结构体变量
内核定时器需要一个timer_list结构体(#include<linux/timer.h>),该结构体指定的内核定时器处理函数等
struct timer_list {
struct list_head entry; //定时器链表入口
unsigned long expires; //以jifffies为单位的定时值(过期时间)
struct tvec_base *base; // 定时器内部值,用户不要使用
void (*function)(unsigned long); // 定时器处理函数
unsigned long data; //传给处理函数的长整形参数值
int slack; //与expires组合成新的expires,在第二部会初始化这个变量
#ifdef CONFIG_TIMER_STATS
int start_pid;
void *start_site;
char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
2.初始化内核定时器(实际初始化timer_list 结构体)
初始化内核定时器需要使用init_timer宏(#include<linux/timer.h>),该宏原型如下:
#define init_timer(timer) init_timer_key((timer), NULL, NULL)
其中timer就是timer_list的指针,init_timer主要调用了init_timer_key函数
void init_timer_key(struct timer_list *timer, const char *name, struct lock_class_key *key)
{
debug_init(timer);
__init_timer(timer, name, key);
}
static void __init_timer(struct timer_list *timer,
const char *name,
struct lock_class_key *key)
{
timer->entry.next = NULL;
timer->base = __raw_get_cpu_var(tvec_bases);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
timer->start_pid = -1;
memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
3.实现定时器处理函数
定时器处理函数原型如下:
void timer_handle(unsigned long arg) //arg就是 timer_list .data的值
4.对timer_list 成员变量的进一步初始化
初始化function函数和expires的值,到达过期时间expires时执行function函数。
5.激活定时器
定时器激活才能使用。使用add_timer函数才能激活定时器,add_timer函数原型如下:
void add_timer(struct timer_list *timer)
{
BUG_ON(timer_pending(timer));
mod_timer(timer, timer->expires);
}
int mod_timer(struct timer_list *timer, unsigned long expires)
{
expires = apply_slack(timer, expires);
/*
* This is a common optimization triggered by the
* networking code - if the timer is re-modified
* to be the same thing then just return:
*/
if (timer_pending(timer) && timer->expires == expires)
return 1;
return __mod_timer(timer, expires, false, TIMER_NOT_PINNED);
}
6.停止定时器
如果要在定时器到期之前停止定时器,可以使用如下函数:
int del_timer(struct timer_list *timer)
int del_timer_sync(struct timer_list *timer)
无论定时器是否被激活都可以使用这两个函数,未被激活是函数返回0,否则返回1,多处理器都在用定时器时使用此函数del_timer_sync
停止定时器。但此函数不能用在中断上下文中,(因为该函数可能引起阻塞)。
内核延迟
1.忙等待
使用方法
unsigned long delay=jiffies + 5 * Hz;
............................................
while(time_before(jiffies,delay))
cond_resched();
2.短延迟
linux/delay.h
ndelay(unsigned long x); //延迟x纳秒
udelay(unsigned long x); //延迟x微秒
mdelay(unsigned long x); //延迟x毫秒
以上几个宏的实现原理是忙等待,会占用大量CPU资源,所以用下面的函数延时较好
msleep(unsigned int msecs) //延迟msecs毫秒
msleep_interruptible(unsigned int msecs) //延迟msecs毫秒,,可被中断打断
ssleep(unsigned int seconds) //延迟seconds秒