[技術] Linux下的Timer

Written on 10:36 上午 by Yu Lai

其實之前就有用到Timer,只是最近又再一次用到,所以把相關的部份整理一下。
在Linux下實做定時器有兩種方法,分別是alarm()和setitimer(),以下分別介紹:

1. alarm()
alarm()可以用來設定信號SIGALRM在指定的秒數後傳到目前的process。
使用起來比起setitimer較為簡單,但由於使用的單位是秒數,
所以如果不要求很精確的話,用alarm()配合signal()就夠了。

unsigned int alarm(unsigned int seconds)

alarm()用來設置信號SIGALRM在經過參數seconds指定的秒數後傳送給目前的process。
如果參數seconds為0,則之前設置的鬧鐘會被取消,並將剩下的時間返回,
如果之前未設鬧鐘則返回0。

void sigalrm_fn(int sig) {

printf("alarm!\n");
alarm(2);

return;

}

int main(void) {

signal(SIGALRM, sigalrm_fn);
alarm(1);
while(1) pause();

}


2. setitimer()
setitimer()比alarm功能強大,支持3種類型的Timer:
ITIMER_REAL : 以系統真實的時間來計算,它送出SIGALRM信號。
ITIMER_VIRTUAL : 以Process在User-Mode下花費的時間來計算,它送出SIGVTALRM信號。
ITIMER_PROF : 以Process在User-Mode下和Kernel-Mode下所費的時間來計算,它送出SIGPROF信號。

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));

setitimer()第一個參數which指定定時器類型(上面三種之一);
第二個參數是struct itimerval的一個instance;第三個參數可不做處理。
setitimer()調用成功返回0,否則返回-1。

struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};

itimerval結構中的it_value是減少的時間,當這個值為0的時候就發出相應的信號。
然後再將it_value設置為it_interval值。也就是說如果itimerval.it_interval不為0,
則該定時器將持續有效(每隔一段時間就會發送一個信號)。

下面是關於setitimer的一個簡單示範,在該例子中,
每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM信號:
int sec;

void sigroutine(int signo) {

switch (signo){
case SIGALRM:
printf("Catch a signal -- SIGALRM \n");
signal(SIGALRM, sigroutine);
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM \n");
signal(SIGVTALRM, sigroutine);
break;
}
return;

}

int main() {

struct itimerval value, ovalue, value2;
sec = 5;

printf("process id is %d\n", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);

value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue);

value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);

for(;;) ;

}


PS:
Linux信號機制基本上是從Unix系統中繼承過來的。
早期Unix系統中的信號機制比較簡單和原始,後來在實踐中暴露出一些問題,
因此,把那些建立在早期機制上的信號叫做"不可靠信號",
信號值小於SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。
這就是"不可靠信號"的來源。它的主要問題是:Process每次處理信號後,
就將對信號的響應設置為Default動作。在某些情況下,將導致對信號的錯誤處理;
因此,User如果不希望這樣的操作,那麼就要在信號處理函數結尾再一次使用signal(),重新安裝該信號。

If you enjoyed this post Subscribe to our feed

2 Comments

  1. RR2 |

    請問若是不在signal(SIGALRM, target_handler)
    後面接上
    alarm(secs);

    會發生什麼事情??

     
  2. Yu Lai |

    答案是: 不會怎麼樣.
    signal(SIGALRM, target_handler)是將SIGALRM這個signal交由target_handler()去處理, 一般Process預設是ignore掉這個signal的.
    而alarm()則是時間到了會發送SIGALRM這個signal給該Process, 你不接alarm()自然就沒signal過來而已.

     

張貼留言