一.测度时间差
时钟中断是由系统的定时硬件以周期性的时间间隔形成,这个间隔(即频度)由内核按照HZ来确定,HZ是一个与体系结构无关的常量(定义在),可配置(50-1200),在X86平台,默认值为1000.HZ的涵义是系统每秒钟形成的时钟中断的次数.
每每时钟中断发生时,全局变量jiffies(一个32位的unsignedlong变量,定义在)就加1,因而jiffies记录了字linux系统启动后时钟中断发生的次数.驱动程序常借助jiffies来估算不同风波间的时间间隔.
内核提供了一组宏拿来比较时间量:
#include
inttime_after(unsignedlonga,unsignedlongb);
inttime_before(unsignedlonga,unsignedlongb);
inttime_after_eq(unsignedlonga,unsignedlongb);
inttime_after_eq(unsignedlonga,unsignedlongb);
这几个宏可以理解为a宏名b?1:0.
获取当前时间:
#include
structtimeval{
time_ttv_sec;
suseconds_ttv_usec;
};
voiddo_gettimeofday(structtimeval*tv)
二.内核定时器
内核定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行.内核定时器注册的处理函数只执行一次.处理之后即失效.
当内核定时器被调度运行时,几乎可以肯定其不会在注册那些函数的进程正在执行时.相反,它会异步的执行.这些异步类似于硬件中断发生时的情境.实际上,内核定时器是被"软件中断"调度运行的.因而,其运行于原子上下文中.这点和tasklet很类似.处于原子上下文的进程有一些运行时的限制:
1.不能访问用户空间.由于没有进程上下文.未能与特定的进程与用户关联
2.不能执行调度或休眠.
3.Current表针在原子模式下无意义.
内核定时器被组织成单向数组,使用structtimer_list结构描述.
structtime_list{
unsignedlongexpires;//超时的jiffies值
void(*function)(unsignedlong);//注册的定时器处理函数
unsignedlongdata;//定时器处理函数的参数
这3个数组表示,当jiffies等于expires时,内核会调度function函数运行.data是传递给function的参数的表针,倘若function函数须要不止一个参数,这么可以将这几个参数组成一个结构体,并将结构体的表针形参给data.
三.管理定时器的插口
voidinit_timer(structtime_list*timer);
初始化定时器队列结构.timer_list结构在使用前必须初始化,这是要保证结构体中其他的成员能正确形参.
voidadd_timer(structtime_list*timer);
启动定时器.
intdel_timer(structtime_list*timer);
在定时器超时前将定时器删掉.当定时器超时后,系统会手动将其删掉.
四.内核定时器的使用方式
初始化
在使用structtimer_list之前,须要初始化该数据结构,确保所有的数组都被正确地设置。初始化有两种方式。
方式一:
DEFINE_TIMER(timer_name,function_name,expires_value,data);
该宏会静态创建一个名叫timer_name内核定时器,并初始化其function,expires,name和base数组。
方式二:
structtimer_listmytimer;
setup_timer(&mytimer,(*function)(unsignedlong),unsignedlongdata);
mytimer.expires=jiffies+5*HZ;
方式3:
structtimer_listmytimer;
init_timer(&mytimer);
mytimer->timer.expires=jiffies+5*HZ;
mytimer->timer.data=(unsignedlong)dev;
mytimer->timer.function=&corkscrew_timer;/*timerhandler*/
通过init_timer()动态地定义一个定时器,随后,将处理函数的地址和参数绑定给一个timer_list,
注意linux使用定时器,无论用哪种方式初始化,其本质都只是给数组形参,所以只要在运行add_timer()之前linux 软件,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)
这个函数拿来判定一个定时器是否被添加到了内核数组中以等待被调度运行。注意,当一个定时器函数即即将被运行前,内核会把相应的定时器从内核数组中删掉(相当于注销)
当删掉定时器时,必须留神一个潜在的竞争条件。当del_timer()返回后linux使用定时器,可以保证的只是:定时器不会再被激活(也就是,将来不会执行),并且在多处理机器上定时器中断可能早已在其他处理器上运行了,所以删掉定时器时须要等待可能在其他处理器上运行的定时器处理程序都退出虚拟主机 linux,这时就要使用del_timer_sync()函数执行删掉工作:
del_timer_sync(&my_timer);
和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用.
非常注意:
非常要注意次序:提供两个次序参考:
一:
structtimer_listmytimer;init_timer(&mytimer);mytimer.function=mytimer_function;mytimer.expires=jiffies+HZ/100add_timer(&mytimer);
mytimer.data的值可以在第五条之前设置,也可以不设置;看自己用不用传递参数。注意jiffies这个的数值是仍然在变的所以第四天和第五条不要离太远!!!!
再给一个次序
二:
structtimer_listmytimer;init_timer(&mytimer);mytimer.function=mytimer_function;add_timer(&mytimer);//没有设置expires主键。默认为0,立刻执行mytimer_function;。。。。。。。。。。。。mod_timer(&mytimer,jiffies+HZ/100);//设定expires数组。重启定时器;
注意jiffies这个的数值是仍然在变的所以