您的位置:首页 > 其它

内核定时器

2010-08-19 23:37 239 查看
转自:http://blog.chinaunix.net/u2/73528/showart_1131053.html

一、定义:

/include/linux/timer.h

struct
timer_list {

struct
list_head entry;

unsigned
long expires;

void
(*function)(unsigned long);

unsigned
long data;

struct
tvec_t_base_s *base;

#ifdef
CONFIG_TIMER_STATS

void
*start_site;

char
start_comm[16];

int
start_pid;

#endif

};

二、作用:

一个
timer_list
结构体的实例对应一个定时器,在
linux
设备驱动编程中,可以使用
timer_list
和基于它的一些操作
(
函数
)
来完成定时出发工作或者完成某周期性的事物。

三、个字段详解:

1

struct
list_head entry;

定时器链表,用于存放软定时器,该链表根据定时器
expirex
字段的值将它们分组存放。

2

unsigned
long expires;

定时器的到期时间,到达
expires
时间后,定时器将调用其成员函数
function
,其中将
data
字段作为
function
的参数。该字段表示的时间是以时间节拍为单位。例如如果你想定时一秒,则
expires=jiffies+HZ*1
。关于
jiffies
的详解见: http://blog.chinaunix.net/u2/73528/showart_1130865.html
3

void
(*function)(unsigned long);

定时器处理函数,也就是说到达
expires
时间时,
function
函数将被调用执行。起参数来自定时器的
data
字段。

4

unsigned
long data;

在调用
function
函数时,该字段作为其参数被使用。

四、操作:

1
、定义及初始化:

(1)

struct
timer_list timer;

void
init_timer(struct timer_list *timer);

init_timer()
函数被定义在
kernel/timer.c
中,实际上是将
timer

entry

next
指针置为
NULL,

base
字段赋值。

(2)

struct
timer_list timer;

timer=TIMER_INITIALIZER(function,expires,data);

采用这种初始化方式,必须首先先写好定时器处理函数
function.
TIMER_INITIALIZER
宏的定义如下:

#define
TIMER_INITIALIZER(_function, _expires, _data) { /

.function
= (_function), /

.expires
= (_expires), /

.data
= (_data), /

.base
= &boot_tvec_bases, /

}

其中
boot_tcec_bases
是在
kernel/timer
中定义的一个全局的
tvec_t_base_s
类型的变量。

(3)

DEFINE_TIMER(timer,function,expires,data);

定义并初始化定时器
timer,
相当于
(2).
其中
DEFINE_TIMER
宏的定义为:

#define
DEFINE_TIMER(_name, _function, _expires, _data) /

struct
timer_list _name = /

TIMER_INITIALIZER(_function, _expires,
_data
)

(4)

struct
timer_list timer;

setup_timer(&timer);

等同于定义方式
(2)

(3)
,不过对
base
字段的赋值是调用了
init_timer()
函数。
setup_timer()
原型为:

static
inline void setup_timer(struct timer_list * timer,

void
(*function)(unsigned long),

unsigned long data)

{

timer->function
= function;

timer->data
= data;

init_timer(timer);

}

2
、注册定时器:

在定义并初始化了定时器之后,就要调用
add_timer()
函数来将该定时器注册到内核中,这样定时器才会工作。在注册之后,定时器就开始计时,在到达时间
expires
时,执行回调函数
function(->data)

add_timer()
函数的原型为:

<!--
@page { size: 21.59cm 27.94cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
static
inline void add_timer(struct timer_list *timer)

{

BUG_ON(timer_pending(timer));

__mod_timer(timer,
timer->expires);

}

3
、删除定时器:

int
del_timer(struct timer_list *timer);

从内核中删除已经注册的定时器
timer
。如果该定时器是活动的,则返回
1
,否则返回
0


int
del_timer(struct timer_list *timer)

{

tvec_base_t
*base;

unsigned
long flags;

int
ret = 0;

timer_stats_timer_clear_start_info(timer);

if
(timer_pending(timer)) {

base
= lock_timer_base(timer, &flags);

if
(timer_pending(timer)) {

detach_timer(timer, 1);

ret
= 1;

}

spin_unlock_irqrestore(&base->lock,
flags);

}

return
ret;

}

4
、修改定时器的定时时间:

int
mod_timer(struct timer_list *timer, unsigned long expires)

{

BUG_ON(!timer->function);

timer_stats_timer_set_start_info(timer);

/*

*
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->expires == expires && timer_pending(timer))

return
1;

return
__mod_timer(timer, expires);

}

从代码可以看出,如果所给的要修改的时间等于定时器原来的时间并且定时器现在正处于活动状态,则不修改,返回
1
,否则修改定时器时间,返回
0

mod_timer()
是一个非有效的更新处于活动状态的定时器的时间的方法,如果定时器处于非活动状态,则会激活定时器。在功能上,
mod_timer()
等价于:

del_timer(timer);

timer->expires=expires;

add_timer(timer);

五、内核延时函数:

1
、短延时:

ndelay(unsigned
long nsecs); /*
延时
nsecs
纳秒
*/

udelay(unsigned
long usecs); /*
延时
usecs
微秒
*/

mdelay(unsigned
long msecs); /*
延时
msecs
毫秒
*/

此三个宏延时的本质是“忙等待”,也就是说在延时的过程中,并没有放弃
CPU
,而是根据
CPU
的频率进行一定次数的循环来达到延时的目的。三个宏最终都是将各自的参数(延时的时间)经过一定的换算调用
delay_loop()

函数来循环耗时达到延时,
delay_loop()
如下:

static
void delay_loop(unsigned long loops)

{

int
d0;

__asm__
__volatile__(

"/tjmp
1f/n"

".align
16/n"

"1:/tjmp
2f/n"

".align
16/n"

"2:/tdecl
%0/n/tjns 2b"

:"=&a"
(d0)

:"0"
(loops));

}

可以明显的看到每次自减
loops
,然后判断,如果为
0
,则结束,否则跳到标号
2
处,形成循环。这就是所谓的“忙等待”。

2
、长延时:

在内核中,一个直观的延时的方法是将所要延迟的时间设置的当前的
jiffies
加上要延迟的时间,这样就可以简单的通过比较当前的
jiffies
和设置的时间来判断延时的时间时候到来。针对此方法,内核中提供了简单的宏用于判断延时是否完成。

time_after(jiffies,delay);
/*
此刻如果还没有到达延时的时间,则返回真,否则返回
0*/

time_before(jiffies,delay);/*
如果延时还没有完成,则返回真,否则返回
0*/

其中
time_after

time_before
分别被定义为:

#define
time_after(a,b) /

(typecheck(unsigned
long, a) && /

typecheck(unsigned
long, b) && /

((long)(b)
- (long)(a) < 0))

#define
time_before(a,b) time_after(b,a)

在具体使用中也是将
time_after
或者
time_before
作为
while
循环的判断语句,进行忙等待延时。

3
、睡眠延时:

与忙等待延时相对的是睡眠延时,在延时的过程中,进程是处于睡眠状态,这意味着其他的任务可以在这是被调度执行,提高了
CPU
的有效利用率。在睡眠给定的时间后,任务又被重新调度执行。内核提供的睡眠延时函数是:

void
msleep(unsigned int msecs)


unsigned
long msleep_interruptible(unsigned int msecs)


则会两个函数的区别是调用
msleep()
函数进行睡眠延时的进程不能被信号打断,而调用
msleep_interruptible()
函数延时的进程可以被信号唤醒。一下给出
msleep()
函数的代码:

void
msleep(unsigned int msecs)

{

unsigned
long timeout = msecs_to_jiffies(msecs) + 1;

while
(timeout)

timeout
= schedule_timeout_uninterruptible(timeout);

}

可以看出
msleep()
本质还是依靠
schedule_timeout_uninterruptible()
函数实现的。在每次被重新调度执行时,如果睡眠没有完成,则重新进入睡眠直到到达睡眠的时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: