使用定時器的目的是為了周期性的執行某一任務,或者是到了某個指定時間去執行某一任務。要達到這一目的,一般有兩個常見的方法。一個是用linux內部的三個定時器,另一個是用sleep, usleep函數讓進程睡眠一段時間,其實,還有一個方法,那就是用gettimeofday, difftime等自己來計算時間間隔,然后時間到了就執行某一任務,但是這種方法效率低,所以不常用。
法一
首先來看看linux操作系統為每一個進程提供的3個內部計時器。
- ITIMER_REAL: 給一個指定的時間間隔,按照實際的時間來減少這個計數,當時間間隔為0的時候發出SIGALRM信號
- ITIMER_VIRTUAL: 給定一個時間間隔,當進程執行的時候才減少計數,時間間隔為0的時候發出SIGVTALRM信號
- ITIMER_PROF: 給定一個時間間隔,當進程執行或者是系統為進程調度的時候,減少計數,時間到了,發出SIGPROF信號,這個和ITIMER_VIRTUAL聯合,常用來計算系統內核時間和用戶時間。
用到的函數有:
#include <sys/time.h>
int getitimer(int which,struct itimerval *value);
int setitimer(int which,struct itimerval*newvalue,struct itimerval* oldvalue);
strcut timeval {
long tv_sec;/*秒*/
long tv_usec;/*微秒*/
};
struct itimerval
{
struct timeval it_interval;/*時間間隔*/
struct timeval it_value;/*當前時間計數*/
};
it_interval用來指定每隔多長時間執行任務, it_value用來保存當前時間離執行任務還有多長時間。比如, 你指定it_interval為2秒(微秒為0),開始的時候我們把it_value的時間也設定為2秒(微秒為0),當過了一秒, it_value就減少一個為1, 再過1秒,則it_value又減少1,變為0,這個時候發出信號(告訴用戶時間到了,可以執行任務了),並且系統自動把it_value的時間重置為 it_interval的值,即2秒,再重新計數。 為了幫助你理解這個問題,我們來看一個例子:
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
staticchar msg[]= “time is running out\n”;
staticint len;
// 向標准錯誤輸出信息,告訴用戶時間到了
void prompt_info(int signo){
write(STDERR_FILENO, msg, len);}// 建立信號處理機制void init_sigaction(void){
struct sigaction tact;
/*信號到了要執行的任務處理函數為prompt_info*/
tact.sa_handler= prompt_info;
tact.sa_flags=0;
/*初始化信號集*/
sigemptyset(&tact.sa_mask);
/*建立信號處理機制*/
sigaction(SIGALRM,&tact, NULL);
}void init_time(){
struct itimerval value;
/*設定初始時間計數為2秒0微秒*/
value.it_value.tv_sec=2;
value.it_value.tv_usec=0;
/*設定執行任務的時間間隔也為2秒0微秒*/
value.it_interval= value.it_value;
/*設置計時器ITIMER_REAL*/
setitimer(ITIMER_REAL,&value, NULL);
}int main(){
len =strlen(msg);
init_sigaction();
init_time();
while(1);
exit(0);}
該程序的ITMER_REAL定時器,每隔2秒鍾都會發送一個SIGALRM信號,當主函數接收到了這個信號之后,調用信號處理函數 prompt_info在標准錯誤上輸出time is running out這個字符串。
ITIMER_VIRTUAL和ITIMER_PROF的使用方法類似,當你在setitimer里面設置的定時器為 ITIMER_VIRTUAL的時候,你把sigaction里面的SIGALRM改為SIGVTALARM, 同理,ITIMER_PROF對應SIGPROF。
不過,你可能會注意到,當你用ITIMER_VIRTUAL和ITIMER_PROF的時候,你拿一個秒表,你會發現程序輸出字符串的時間間隔會不止2秒,甚至5-6秒才會輸出一個,至於為什么,自己好好琢磨一下。
如果函數init_sigaction報錯:
解決方法:在文件最開頭加上如下代碼(在所有#include之前)
#define _XOPEN_SOURCE
法二
下面我們來看看用sleep以及usleep怎么實現定時執行任務。
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
staticchar msg[]= “I received a msg.\n”;
int len;
void show_msg(int signo){
write(STDERR_FILENO, msg, len);}int main(){
struct sigaction act;
union sigval tsval;
act.sa_handler= show_msg;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(50,&act, NULL);
len =strlen(msg);
while(1){ sleep(2);/*睡眠2秒*/
/*向主進程發送信號,實際上是自己給自己發信號*/
sigqueue(getpid(),50, tsval);
}return0;}
看到了吧,這個要比上面的簡單多了,而且你用秒表測一下,時間很准,指定2秒到了就給你輸出一個字符串。所以,如果你只做一般的定時,到了時間去執行一個任務,這種方法是最簡單的。
法三
下面我們來看看,通過自己計算時間差的方法來定時:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
staticchar msg[]= “I received a msg.\n”;
int len;
static time_t lasttime;
void show_msg(int signo){
write(STDERR_FILENO, msg, len);}int main(){
struct sigaction act;
union sigval tsval;
act.sa_handler= show_msg;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(50,&act, NULL);
len =strlen(msg);
time(&lasttime);
while(1){
time_t nowtime;
/*獲取當前時間*/
time(&nowtime);
/*和上一次的時間做比較,如果大於等於2秒,則立刻發送信號*/
if(nowtime – lasttime >=2)
{
/*向主進程發送信號,實際上是自己給自己發信號*/
sigqueue(getpid(),50, tsval);
lasttime = nowtime;}}return0;}
這個和上面不同之處在於,是自己手工計算時間差的,如果你想更精確的計算時間差,你可以把 time 函數換成gettimeofday,這個可以精確到微妙。(注:1秒=1000毫秒ms =106微秒µs) 上面介紹的幾種定時方法各有千秋,在計時效率上、方法上和時間的精確度上也各有不同,采用哪種方法,就看你程序的需要。
[編輯]附:itimerval時鍾的使用
#include<stdio.h>
#include<signal.h>
#include<sys/time.h>//itimerval結構體的定義
int handle_count=0;
void set_time(void){struct itimerval itv;
itv.it_interval.tv_sec=10;//自動裝載,之后每10秒響應一次
itv.it_interval.tv_usec=0;
itv.it_value.tv_sec=5;//第一次定時的時間
itv.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&itv,NULL);
} void alarm_handle(int sig){ handle_count++;
printf("have handle count is %d\n",handle_count);
} void main(void)
{struct itimerval itv;
signal(SIGALRM,alarm_handle);
set_time();
while(1){
getitimer(ITIMER_REAL,&itv);
printf("pass second is %d\n",(int)itv.it_value.tv_sec);
sleep(1);
} return;}