使用定時器的目的無非是爲了週期性的執行某一任務,或者是到了一個指定時間去執行某一個任務。要達到這一目的,一般有兩個常見的比較有效的方法。一個是用linux內部的三個定時器,另一個是用sleep, usleep函數讓進程睡眠一段時間,使用alarm定時發出一個信號,還有那就是用gettimeofday, difftime等自己來計算時間間隔,然後時間到了就執行某一任務,但是這種方法效率低,所以不常用。
alarm
alarm用在不需要經確定時的時候,返回之前剩餘的秒數。
NAME
alarm - set an alarm clock for delivery of a signal
SYNOPSIS
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
DESCRIPTION
alarm arranges for a SIGALRM signal to be delivered to the process in
seconds seconds.
If seconds is zero, no new alarm is scheduled.
In any event any previously set alarm is cancelled.
測試程序:
1 | cat timer.c |
2 | #include <stdio.h> |
3 | #include <unistd.h> |
4 | #include <sys/time.h> |
5 | #include <signal.h> |
6 | |
7 | void func() |
8 | { |
9 | printf("2sreached.\n"); |
10 | } |
11 | |
12 | int main() |
13 | { |
14 | signal(SIGALRM,func); |
15 | alarm(2); |
16 | while(1); |
17 | return 0; |
18 | } |
19 |
Linux內置的3個定時器
Linux爲每個任務安排了3個內部定時器:
ITIMER_REAL:實時定時器,不管進程在何種模式下運行(甚至在進程被掛起時),它總在計數。定時到達,向進程發送SIGALRM信號。
ITIMER_VIRTUAL:這個不是實時定時器,當進程在用戶模式(即程序執行時)計算進程執行的時間。定時到達後向該進程發送SIGVTALRM信號。
ITIMER_PROF:進程在用戶模式(即程序執行時)和核心模式(即進程調度用時)均計數。定時到達產生SIGPROF信號。ITIMER_PROF記錄的時間比ITIMER_VIRTUAL多了進程調度所花的時間。
定時器在初始化是,被賦予一個初始值,隨時間遞減,遞減至0後發出信號,同時恢復初始值。在任務中,我們可以一種或者全部三種定時器,但同一時刻同一類型的定時器只能使用一個。
用到的函數有:
#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秒,再重新計數。
爲了幫助你理解這個問題,我們來看一個例子:
1 | #include <stdio.h> |
2 | #include <signal.h> |
3 | #include <sys/time.h> |
4 | |
5 | /* |
6 | ******************************************************************************************************* |
7 | ** Function name: main() |
8 | ** Descriptions : Demo for timer. |
9 | ** Input : NONE |
10 | ** Output : NONE |
11 | ** Created by : Chenxibing |
12 | ** Created Date : 2005-12-29 |
13 | **----------------------------------------------------------------------------------------------------- |
14 | ** Modified by : |
15 | ** Modified Date: |
16 | **----------------------------------------------------------------------------------------------------- |
17 | ******************************************************************************************************* |
18 | */ |
19 | int limit =10; |
20 | /* signal process */ |
21 | void timeout_info(int signo) |
22 | { |
23 | if(limit ==0) |
24 | { |
25 | printf("Sorry,timelimitreached.\n"); |
26 | return; |
27 | } |
28 | printf("only%dsencondsleft.\n", limit--); |
29 | } |
30 | |
31 | /* init sigaction */ |
32 | void init_sigaction(void) |
33 | { |
34 | struct sigaction act; |
35 | |
36 | act.sa_handler = timeout_info; |
37 | act.sa_flags =0; |
38 | sigemptyset(&act.sa_mask); |
39 | sigaction(SIGPROF, &act, NULL); |
40 | } |
41 | |
42 | /* init */ |
43 | void init_time(void) |
44 | { |
45 | struct itimerval val; |
46 | |
47 | val.it_value.tv_sec =1; |
48 | val.it_value.tv_usec =0; |
49 | val.it_interval = val.it_value; |
50 | setitimer(ITIMER_PROF, &val, NULL); |
51 | } |
52 | |
53 | |
54 | int main(void) |
55 | { |
56 | init_sigaction(); |
57 | init_time(); |
58 | printf("Youhaveonly10secondsforthinking.\n"); |
59 | |
60 | while(1); |
61 | return 0; |
62 | } |
63 |
對於ITIMER_VIRTUAL和ITIMER_PROF的使用方法類似,當你在setitimer裏面設置的定時器爲ITIMER_VIRTUAL的時候,你把sigaction裏面的SIGALRM改爲SIGVTALARM, 同理,ITIMER_PROF對應SIGPROF。
不過,你可能會注意到,當你用ITIMER_VIRTUAL和ITIMER_PROF的時候,你拿一個秒錶,你會發現程序輸出字符串的時間間隔會不止2秒,甚至5-6秒纔會輸出一個,至於爲什麼,自己好好琢磨一下^_^
sleep
下面我們來看看用sleep以及usleep怎麼實現定時執行任務。
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
static char 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);
}
return 0;
}
看到了吧,這個要比上面的簡單多了,而且你用秒錶測一下,時間很準,指定2秒到了就給你輸出一個字符串。所以,如果你只做一般的定時,到了時間去執行一個任務,這種方法是最簡單的。
時間差
下面我們來看看,通過自己計算時間差的方法來定時:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
static char 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;
}
}
return 0;
}