由於學習LINUX環境高級編程信號章節。課後第5小題要求實現這個功能。所以嘗試實現了一下,並做記錄 ...
本例子參考 Don Libes的Title: Implementing Software Timers例子改寫
為什麼需要這個功能,因為大多數電腦軟體時鐘系統通常只能有一個時鐘觸發一次中斷。當運行多個任務時,我們會想要多個定時器 的時鐘跟蹤併發這樣可以生成正確的時間重疊,操作系統這樣做。
本例子是為了實現使用Linux下的一個定時器,實現任一數量的定時器功能。
首先我們需要一些數據類型用來描述時鐘數據結構
#include <stdio.h> #include<time.h> #define TRUE 1 #define FALSE 0 #define MAX_TIMERS ... 最大時鐘數量 typedef timerval TIME; 定義時間類型 #define VERY_LONG_TIME ... 最大時間長度 struct timer { int inuse; 時鐘是否可用 TIME time; 定時時間長度 char *event; 是否超時 } timers[MAX_TIMERS]; /* set of timers */
每個定時器都以這個數據結構來描述,第一個成員用來描述時鐘是否正在使用,第二個成員是這個定時器的定時時間,第三個成員是是一個指針,*event初始化應該為0,當他被置為1,我們知道這個定時器已經超時了,和他相關的任務可以執行。
接下來是定時器數組的初始化,這裡將每個時鐘inuse成員設置為FALSE,表示時鐘不可用。
void timers_init() { struct timer *t; for (t=timers;t<&timers[MAX_TIMERS];t++) t->inuse = FALSE; }
現在開始是結構實現部分
首先寫到的timer_undeclare這個函數,這個函數與後面的timer_declare相對立。主要作用是清除一個定時器。
有很多方法可以用來保存定時器的定時記錄。沒有複雜時鐘硬體的機器通常在每一個時鐘周期處理一個中斷處理程式。然後軟體就在處理程式中獲取系統時間,然後判斷是否設置的定時器超時。
很多比較聰明的機器可以在硬體中設置定時時間,一旦時間超時,就觸發一個硬體中斷。這同樣適用與軟體中斷。
他們通過一個 定義一個time_now來記錄當前的系統時間,volatile告訴機器每次從寄存器取值,防止數據被系統優化。
volatile TIME time_now
接下來定義一系列數據來記錄 timer_next 指接下來要我們想要計時的定時器。time_timer_set保存最後一次獲取的系統時間。
struct timer *timer_next = NULL;/* timer we expect to run down next */ TIME time_timer_set; /* time when physical timer was set */ //取消一個定時器 void timer_undeclare(struct timer *t) { disable_interrupts(); if(!t->inuse) { enable_interrupts(); return ; } t->inuse=0; if(t==timer_next) { if(time(&time_now)<0) perror("time error"); timers_update(time_now-time_timer_set); if(timer_next) { start_physical_timer(&timer_next->time); time_timer_set=time_now; } } enable_interrupts(); }
timer_undeclare作用為取消一個定時器。首先讓中斷失效,這很重要,因為時鐘數據結構數據是在各進程中共用的,是可以在其他中斷中被修改的,為了防止不必要的錯我,這個取消操作應該為一個原子操作。開始我們先檢測是否這個時鐘已經無效了。如果有效,則設置inuse使其失效。如果我們要取消的定時器正好是下一個期望等待的定時器。那我們要重新指定下一個期望等待的定時器。在此之前所有定時器都要更新一下前一個定時器已經走過的時間。
接下來我們看到timers_update(time_t ti)函數
//更新定時器表時間 void timers_update(time_t time) { static struct timer timer_last={ 0, {}, NULL }; timer_last.time.tv_sec=10; struct timer *t; timer_next=&timer_last; for(t=timers;t<&timers[MAX_TIMERS];t++) { if(t->inuse) { if(time<t->time.tv_sec){ t->time.tv_sec-=time; if(t->time.tv_sec<\ timer_next->time.tv_sec) timer_next=t; }else { *(t->event)=1; t->inuse=0; } } } if(!timer_next->inuse)timer_next=0; }
此函數作用是更新所有有效定時器的時間長,同時將timer_next指向當前延時時間最短的一個定時器。如沒有定時器,則將timer_next設置為空。
timer_declare 加入一個定時器
struct timer * timer_declare(TIME *ti,char *event) { struct timer *t; disable_interrupts(); for(t=timers;t<&timers[MAX_TIMERS];t++) { if(!t->inuse)break; } if(t==&timers[MAX_TIMERS]) { enable_interrupts(); return 0; } t->event=event; t->time.tv_sec=ti->tv_sec; t->time.tv_usec=ti->tv_usec; if(!timer_next) { if(time(&time_now)<0) perror("time() error"); time_timer_set=time_now; start_physical_timer(&((timer_next=t)->time)); }else if((ti->tv_sec+time_now)<(\ timer_next->time.tv_sec+time_timer_set)) { if(time(&time_now)<0) perror("time error"); timers_update(time_now-time_timer_set); time_timer_set=time_now; start_physical_timer(&((timer_next=t)->time)); }else { } t->inuse=1; enable_interrupts(); return t; }
首先找到一個可用的定時器表項,設置相關參數。
接下來判斷如果timer_next為空,那麼說明定時器表項沒有定時器需要定時,那我們直接將timer_next指向新加入定時器,開始計時。
如果新加入定時器需要延時時間比當前正在延時的定時器的剩餘時間還要短,則更新定時器表,並計時當前加入的定時器。
在處理完當前定時器事件後,將新加入的定時器的inuse置1.
接下來是定時器中斷處理函數
//定時器中斷處理函數 void timer_interrupt_hander(int signo) { printf("interrupt_hander\n"); if(time(&time_now)<0) perror("time() error"); timers_update(time_now-time_timer_set); if(timer_next) { time_timer_set=time_now; start_physical_timer(&timer_next->time); } }
這裡我們列印一串字元來證明定時器時間的觸發,首先要做的先更新定時器表,然後將time_timer_set設置成當前系統時間,繼續進行下一個定時器事件,直到所有定時器都處理完畢。
接下來幾個是LINUX系統相關函數
//失效定時器中斷 void disable_interrupts() { sigset_t new_mask; sigemptyset(&new_mask); sigaddset(&new_mask,SIGALRM); if(sigprocmask(SIG_BLOCK,&new_mask,NULL)<0) perror("SIG_BLOCK error"); } //使能定時器中斷 void enable_interrupts() { sigset_t new_mask; sigemptyset(&new_mask); sigaddset(&new_mask,SIGALRM); if(sigprocmask(SIG_UNBLOCK,&new_mask,NULL)<0) perror("SIG_UNBLOCK error"); } //開啟一個定時器工作 void start_physical_timer(TIME* time) { if(signal(SIGALRM,timer_interrupt_hander)==SIG_ERR) perror("signal error"); struct itimerval new_value; sigset_t zero_mask; sigemptyset(&zero_mask); new_value.it_value.tv_sec=time->tv_sec; new_value.it_value.tv_usec=time->tv_usec; new_value.it_interval.tv_sec=0; new_value.it_interval.tv_usec=0; setitimer(ITIMER_REAL,&new_value,NULL); sigsuspend(&zero_mask); }
主函數測試部分
#include<stdio.h> #include<signal.h> #include"multtime.h" #include<stdlib.h> #include<unistd.h> int main() { pid_t pid; TIME time1,time2,time3; time1.tv_sec=6; time1.tv_usec=0; time2.tv_sec=4; time2.tv_usec=0; time3.tv_sec=2; time3.tv_usec=0; timer_init(); if((pid=fork())<0) { perror("fork() error"); } else if(pid==0) { printf("child 1\n"); timer_undeclare(timer_declare(&time1,0)); } else { if((pid=fork())<0) { perror("fork error"); } else if(pid==0) { printf("child 2\n"); timer_undeclare(timer_declare(&time3,0)); } else { printf("parent\n"); timer_undeclare(timer_declare(&time2,0)); } } exit(0); }
實驗結果:
parent child 2 child 1 interrupt_hander interrupt_hander interrupt_hander
本人初學LINUX是個菜鳥,以自己的理解寫的,待深入學習後再完善改進。