轉自:http://blog.163.com/hong_nt/blog/static/10468917120081130103632925/
軟體定時器的主要功能是實現狀態機器的逾時機制和實現基於逾時的計數功能。
由於協議狀態機器運行於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; i<MAX_TIMERS; i++){
if(T.inuse && !T.set){
if(T.expire<delta){
T.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; i<MAX_TIMERS; i++){
if(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;i<MAX_TIMERS;i++){
T.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; i<MAX_TIMERS; i++){
if(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(i<N_TIMERS){
T.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]);就可以了