Linux内核定时器综述Word文档格式.docx
- 文档编号:6203736
- 上传时间:2023-05-06
- 格式:DOCX
- 页数:50
- 大小:40.52KB
Linux内核定时器综述Word文档格式.docx
《Linux内核定时器综述Word文档格式.docx》由会员分享,可在线阅读,更多相关《Linux内核定时器综述Word文档格式.docx(50页珍藏版)》请在冰点文库上搜索。
mytimer.expires=jiffies+5*HZ;
方法3:
init_timer(&
mytimer);
mytimer->
timer.expires=jiffies+5*HZ;
timer.data=(unsignedlong)dev;
timer.function=&
corkscrew_timer;
/*timerhandler*/
通过init_timer()动态地定义一个定时器,此后,将处理函数的地址和参数绑定给一个timer_list,
注意,无论用哪种方法初始化,其本质都只是给字段赋值,所以只要在运行add_timer()之前,expires,function和data字段都可以直接再修改。
关于上面这些宏和函数的定义,参见include/linux/timer.h。
注册
定时器要生效,还必须被连接到内核专门的链表中,这可以通过add_timer(structtimer_list*timer)来实现。
重新注册
要修改一个定时器的调度时间,可以通过调用mod_timer(structtimer_list*timer,unsignedlongexpires)。
mod_timer()会重新注册定时器到内核,而不管定时器函数是否被运行过。
注销
注销一个定时器,可以通过del_timer(structtimer_list*timer)或del_timer_sync(structtimer_list*timer)。
其中del_timer_sync是用在SMP系统上的(在非SMP系统上,它等于del_timer),当要被注销的定时器函数正在另一个cpu上运行时,del_timer_sync()会等待其运行完,所以这个函数会休眠。
另外还应避免它和被调度的函数争用同一个锁。
对于一个已经被运行过且没有重新注册自己的定时器而言,注销函数其实也没什么事可做。
inttimer_pending(conststructtimer_list*timer)
这个函数用来判断一个定时器是否被添加到了内核链表中以等待被调度运行。
注意,当一个定时器函数即将要被运行前,内核会把相应的定时器从内核链表中删除(相当于注销)
实现原理
由于linux还不是一个实时的操作系统,因此如果需要更高精度,或者更精确的定时的话,可能就需要打一些实时的补丁,或者用商用版的实时linux,.
这里内的定时器最小间隔也就是1个tick.
这里还有一个要注意的,我这里的分析并没有分析内核新的hrt定时器.这个定时器是MontaVista加入到内核的一个高精度的定时器的实现.
先来看几个相关的数据结构.
///这个是一个最主要的数据结构,表示一个完整的定时器级联表
Java代码
1.struct
tvec_base
{
2.///自旋锁
3.spinlock_t
lock;
4.///表示由本地cpu正在处理的定时器链表
5.struct
timer_list
*running_timer;
6.///这个表示当前的定时器级联表中最快要超时的定时器的jiffer
7.unsigned
long
timer_jiffies;
8.///下面表示了5级的定时器级联表.
9.struct
tvec_root
tv1;
10.struct
tvec
tv2;
11.struct
tv3;
12.struct
tv4;
13.struct
tv5;
14.}
____cacheline_aligned;
structtvec_base{
///自旋锁
spinlock_tlock;
///表示由本地cpu正在处理的定时器链表
structtimer_list*running_timer;
///这个表示当前的定时器级联表中最快要超时的定时器的jiffer
unsignedlongtimer_jiffies;
///下面表示了5级的定时器级联表.
structtvec_roottv1;
structtvectv2;
structtvectv3;
structtvectv4;
structtvectv5;
}____cacheline_aligned;
下面来看tvec和tvec_root的结构:
2.struct
list_head
vec[TVN_SIZE];
3.};
4.
6.struct
vec[TVR_SIZE];
7.};
structtvec{
structlist_headvec[TVN_SIZE];
structtvec_root{
structlist_headvec[TVR_SIZE];
可以看到这两个结构也就是hash链表.每次通过超时jiffies来计算slot,然后插入到链表.这里链表是FIFO的.这里除了tv5外其他几个都是简单的与TVR_MASK按位与计算.
entry;
3.///超时节拍数
4.unsigned
expires;
5.///定时器将要执行的回调函数
6.void
(*function)(unsigned
long);
7.///传递给回调函数的参数
8.unsigned
data;
9.///从属于那个base
*base;
11.};
structlist_headentry;
///超时节拍数
unsignedlongexpires;
///定时器将要执行的回调函数
void(*function)(unsignedlong);
///传递给回调函数的参数
unsignedlongdata;
///从属于那个base
structtvec_base*base;
///定义了一个percpu变量.这里要知道定时器的注册和触发执行一定是在相同的cpu上的.
structtvec_baseboot_tvec_bases;
staticDEFINE_PER_CPU(structtvec_base*,tvec_bases)=&
boot_tvec_bases;
内核注册定时器最终都会通过调用internal_add_timer来实现.具体的工作方式是这样的:
1如果定时器在接下来的0~255个jiffies中到期,则将定时器添加到tv1.
2如果定时器是在接下来的256*64个jiffies中到期,则将定时器添加到tv2.
3如果定时器是在接下来的256*64*64个jiffies中到期,则将定时器添加到tv3.
4如果定时器是在接下来的256*64*64*64个jiffies中到期,则将定时器添加到tv4.
5如果更大的超时,则利用0xffffffff来计算hash,然后插入到tv5(这个只会出现在64的系统).
看下面的图就能比较清晰了:
接下来看源码:
1.static
void
internal_add_timer(struct
*base,
struct
*timer)
2.{
3.///取出超时jiffies
unsigned
expires
=
timer->
5.///得到定时器还有多长时间到期(这里是相比于最短的那个定时器)
6.
idx
-
base->
7.
*vec;
8.
9.///开始判断该把定时器加入到那个队列.依次为tv1到tv5
10.
if
(idx
<
TVR_SIZE)
11.
int
i
&
TVR_MASK;
12.
vec
tv1.vec
+
i;
13.
}
else
1
(TVR_BITS
TVN_BITS))
14.
(expires
>
TVR_BITS)
TVN_MASK;
15.
tv2.vec
16.
2
*
17.
18.
tv3.vec
19.
3
20.
21.
tv4.vec
22.
((signed
long)
0)
23.
/*
24.
Can
happen
you
add
a
timer
with
==
jiffies,
25.
or
set
to
go
off
in
the
past
26.
*/
27.
(base->
timer_jiffies
TVR_MASK);
28.
29.
30.
/*
If
timeout
is
larger
than
0xffffffff
on
64-bit
31.
architectures
then
we
use
maximum
timeout:
32.
33.
0xffffffffUL)
34.
0xffffffffUL;
35.
36.
}
37.
38.
tv5.vec
39.
40.
41.
Timers
are
FIFO:
42.
43.///最终加入链表
44.
list_add_tail(&
entry,
vec);
45.}
staticvoidinternal_add_timer(structtvec_base*base,structtimer_list*timer)
{
///取出超时jiffies
unsignedlongexpires=timer->
///得到定时器还有多长时间到期(这里是相比于最短的那个定时器)
unsignedlongidx=expires-base->
structlist_head*vec;
///开始判断该把定时器加入到那个队列.依次为tv1到tv5
if(idx<
TVR_SIZE){
inti=expires&
TVR_MASK;
vec=base->
tv1.vec+i;
}elseif(idx<
1<
(TVR_BITS+TVN_BITS)){
inti=(expires>
TVR_BITS)&
TVN_MASK;
tv2.vec+i;
(TVR_BITS+2*TVN_BITS)){
(TVR_BITS+TVN_BITS))&
tv3.vec+i;
(TVR_BITS+3*TVN_BITS)){
(TVR_BITS+2*TVN_BITS))&
tv4.vec+i;
}elseif((signedlong)idx<
0){
/*
*Canhappenifyouaddatimerwithexpires==jiffies,
*oryousetatimertogooffinthepast
*/
tv1.vec+(base->
timer_jiffies&
TVR_MASK);
}else{
inti;
/*Ifthetimeoutislargerthan0xffffffffon64-bit
*architecturesthenweusethemaximumtimeout:
if(idx>
0xffffffffUL){
idx=0xffffffffUL;
expires=idx+base->
}
i=(expires>
(TVR_BITS+3*TVN_BITS))&
tv5.vec+i;
}
/*
*TimersareFIFO:
*/
///最终加入链表
list_add_tail(&
entry,vec);
}
这里要知道内核中的软定时器是用软中断来实现的,软中断的注册以及实现可以看我前面的blog,这里就不介绍了.我们来看timer模块的初始化:
1.void
__init
init_timers(void)
3.///主要是初始化boot_tvec_bases(如果是smp,则会初始化所有cpu上的boot_tvec_bases)
err
timer_cpu_notify(&
timers_nb,
(unsigned
long)CPU_UP_PREPARE,
5.
(void
*)(long)smp_processor_id());
init_timer_stats();
BUG_ON(err
NOTIFY_BAD);
9.///注册到cpu的notify
chain(这个我前面的blog也有介绍)
register_cpu_notifier(&
timers_nb);
11.///注册软中断
open_softirq(TIMER_SOFTIRQ,
run_timer_softirq);
13.}
void__initinit_timers(void)
///主要是初始化boot_tvec_bases(如果是smp,则会初始化所有cpu上的boot_tvec_bases)
interr=timer_cpu_notify(&
timers_nb,(unsignedlong)CPU_UP_PREPARE,
(void*)(long)smp_processor_id());
init_timer_stats();
BUG_ON(err==NOTIFY_BAD);
///注册到cpu的notifychain(这个我前面的blog也有介绍)
register_cpu_notifier(&
///注册软中断
open_softirq(TIMER_SOFTIRQ,run_timer_softirq);
ok,接下来我们就来看timer_cpu_notify这个函数,其实这个函数还是定时器注册的cpu的notifychain的action:
notifier_block
__cpuinitdata
timers_nb
2.
.notifier_call
timer_cpu_notify,
5.static
__cpuinit
timer_cpu_notify(struct
*self,
action,
*hcpu)
7.{
cpu
(long)hcpu;
9.
switch(action)
case
CPU_UP_PREPARE:
CPU_UP_PREPARE_FROZEN:
12.///模块初始化的时候就会调用这个函数
(init_timers_cpu(cpu)
0)
return
NOTIFY_BAD;
break;
16.....................................
NOTIFY_OK;
18.}
staticstructnotifier_block__cpuinitdatatimers_nb={
.notifier_call=timer_cpu_notify,
staticint__cpuinittimer_cpu_notify(structnotifier_block*self,
unsignedlongaction,void*hcpu)
longcpu=(long)hcpu;
switch(action){
caseCPU_UP_PREPARE:
caseCPU_UP_PREPARE_FROZEN:
///模块初始化的时候就会调用这个函数
if(init_timers_cpu(cpu)<
0)
returnNOTIFY_BAD;
break;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 内核 定时器 综述
![提示](https://static.bingdoc.com/images/bang_tan.gif)