在linux下的精密定时器
软件定时器的主要功能是实现状态机的超时机制和实现基于超时的计数功能。
由于协议状态机运行于Linux内核之上,也就是说在用户态运行。在Linux系统中,用户态应用程序只能调用三个定时器:ITIMER_REAL、 ITIMER_VIRTUAL和ITIMER_PROF。而在协议状态机中需要32个定时器,所以需要新的机制来完成定时功能。
软件定时器主要被用来解决硬件定时器的物理限制,并为应用程序提供更加灵活的定时器编程接口。定时器最基本的作用就是允许某个任务在将来某个特定时间点运 行。定时器超时,被注册的任务开始运行,在我们的系统中,和定时器相关的任务就是产生某个定时器超时事件。我们定义最基本的定时器结构Timer,当作我 们的软件定时器对象:
#define MAX_TIMERS N //N为你需要的定时器的个数
struct Timer{
long timeout; // 定时器超时时间,单位毫秒ms
long expire; // 下一次中断时定时器剩余的超时时间,单位毫秒ms
int inuse; // 定时器是否被使用
int set; // 定时器是否超时
int signal; // 定时器超时时,发送的事件类型
int param; // 定时器超时时,发送的事件类型的参数
}T[MAX_TIMERS];// 定时器集合
软件定时器提供给应用程序的接口主要有4个:
Int init_timers(); // 初始化定时器集合
Int start_timer(struct Timer *T); // 启动定时器T
Int stop_timer(struct Timer *T); // 停止定时器T
Int get_event(); // 查询超时的定时器事件
程序初始化时调用init_timers()初始化定时器集合,初始化之后就可以在系统中调用start_timer()和stop_timer()来使用定时器功能。协议状态机循环调用get_event()查询超时的定时器事件,输入到协议状态机内部。
软件定时器基于一个内部的由Linux操作系统提供的定时器ITIMER_REAL,实际的定时工作都由该定时器完成。定时器ITIMER_REAL每次 超时时,调用update_timers()更新定时器集合,将超时的定时器的set位置位,并查询定时器集合中所有已经启动的定时器,选择expire 值最小的那个定时器,用该expire值来重置定时器ITIMER_REAL,并更新定时器集合中所有已启动定时器的expire值。也就是说在该 expire毫秒后,定时器ITIMER_REAL将重新发生超时中断,重复以上过程。如果没有软件定时器被启动,定时器ITIMER_REAL将被清 零,直到下一次调用start_timer()。
如果在两次中断之间调用start_timer(struct Timer *T),将会比较T->expire值和离下一次时钟中断的时间间隔time_remain。如果T->expire小于 time_remain,则将把T作为最小定时器,用T->expire重置内部定时器ITIMER_REAL,并更新定时器集合中所有已启动定时 器的expire值;如果T->expire不小于time_remain,则只需更新T的expire值。
stop_timer(struct Timer *T)只需将T的使用标识位insue置零,在更新定时器集合时就会被自动忽略。
部分代码如下:
程序soft_timer.c:
#include "soft_timer.h"
int delta = 10;
long start = 0;
struct itimerval phy_timer;
struct itimerval old_timer;
struct Timer *cur_timer;
void update_timers(int sig)
{
int i = 0;
int min_timer = -1;
long min_expire = 1000000000;
if(cur_timer->inuse)cur_timer->set = 1;
for(i=0; iif(T.inuse && !T.set){
if(T.expireT.set = 1;
}else if(T.expire > 0 && min_expire>T.expire){
min_expire = T.expire;
min_timer = i;
}
}
}
if(min_timer<0){
timerclear(&(phy_timer.it_value));
timerclear(&(phy_timer.it_interval));
}else{
phy_timer.it_value.tv_sec = min_expire/1000;
phy_timer.it_value.tv_usec = (min_expire%1000)*1000;
timerclear(&(phy_timer.it_interval));
cur_timer = &T[min_timer];
for(i=0; iif(T.inuse && !T.set){
T.expire -= min_expire;
}
}
}
setitimer(ITIMER_REAL, &phy_timer, NULL);
}
int create_phy_timer(struct itimerval *timer, void (*handler)(int))
{
int rc = 0;
struct sigaction sa;
memset (&sa, 0, sizeof (sa));
sa.sa_handler = handler;
sigaction(SIGALRM, &sa, NULL);
timerclear(&(timer->it_value));
timerclear(&(timer->it_interval));
setitimer(ITIMER_REAL, timer, NULL);
return rc;
}
int init_timers()
{
int ret = 0;
int i = 0;
for(i=0;iT.inuse = 0;
T.param = 0;
T.set = 0;
T.timeout = 0;
T.expire = 0;
}
cur_timer = &T[0];
create_phy_timer(&phy_timer, update_timers);
}
int start_timer(struct Timer *t)
{
int ret = 0;
int i = 0;
long diff = 0;
long time_remain;
t->expire = t->timeout;
t->inuse = 1;
t->set = 0;
getitimer(ITIMER_REAL, &old_timer);
time_remain = old_timer.it_value.tv_sec*1000+old_timer.it_value.tv_usec/1000;
//printf("time_remain=%ld\n",time_remain);
if(time_remain==0){
phy_timer.it_value.tv_sec = t->timeout/1000;
phy_timer.it_value.tv_usec = (t->timeout%1000)*1000;
timerclear(&(phy_timer.it_interval));
setitimer(ITIMER_REAL, &phy_timer, NULL);
cur_timer = t;
return ret;
}
if(t->timeout+delta<=time_remain){
diff = time_remain - t->timeout;
for(i=0; iif(cur_timer==&T){
cur_timer->expire = diff;
}else if(t==&T){
}else if(T.inuse && !T.set){
T.expire += diff;
}
}
phy_timer.it_value.tv_sec = t->timeout/1000;
phy_timer.it_value.tv_usec = (t->timeout%1000)*1000;
timerclear(&(phy_timer.it_interval));
setitimer(ITIMER_REAL, &phy_timer, NULL);
cur_timer = t;
}else{
t->expire = t->timeout - time_remain;
//printf("t->expire =%ld\n", t->expire);
}
return ret;
}
int stop_timer(struct Timer *t)
{
int ret = 0;
t->inuse = 0;
t->expire = t->timeout;
t->set = 0;
return ret;
}
long current_millis()
{
struct timeval tv;
long now;
gettimeofday(&tv, NULL);
now = (tv.tv_sec%100000)*1000+tv.tv_usec/1000;
return now;
}
void sleep_millis(long ms)
{
struct timespec tv;
tv.tv_sec = ms/1000;
tv.tv_nsec = (long)(ms%1000)*1000000;
nanosleep(&tv, NULL);
}
soft_timer.h头文件如下:
#define MAX_TIMERS N
struct Timer{
long timeout;
long expire;
int inuse;
int set;
int signal;
int param;
}T[MAX_TIMERS];
extern int init_timers();
extern int start_timer(struct Timer *t);
extern int stop_timer(struct Timer *t);
extern int start_phy_timer(struct Timer *t, void (*handler)(int));
extern int stop_phy_timer(struct Timer *t);
extern long current_millis();
extern void sleep_millis(long ms);
#ifdef __cplusplus
}
#endif
#endif /*__soft_timer_h*/
在调用的时候可以先将其初始化
init_timers();
for(i = 1; i< MAX_TIMERS; i++){
if(iT.timeout = timeouts;
T.signal = time_events;
}else{
T.timeout = T[13].timeout;
T.signal = T[13].signal;
}
}
随后在适当的时刻start_timer(&T[n]);或者stop_timer(&T[n]);就可以了