Linux定时器的使用(三种方法)
使用定時器的目的無非是為了周期性的執(zhí)行某一任務,或者是到了一個指定時間去執(zhí)行某一個任務。要達到這一目的,一般有兩個常見的比較有效的方法。一個是用linux內(nèi)部的三個定時器,另一個是用sleep, usleep函數(shù)讓進程睡眠一段時間,使用alarm定時發(fā)出一個信號,還有那就是用gettimeofday, difftime等自己來計算時間間隔,然后時間到了就執(zhí)行某一任務,但是這種方法效率低,所以不常用。
alarm
alarm用在不需要經(jīng)確定時的時候,返回之前剩余的秒數(shù)。
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 aSIGALRMsignal 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 | cattimer.c |
| 2 | #include<stdio.h> |
| 3 | #include<unistd.h> |
| 4 | #include<sys/time.h> |
| 5 | #include<signal.h> |
| 6 | |
| 7 | voidfunc() |
| 8 | { |
| 9 | printf("2sreached. "); |
| 10 | } |
| 11 | |
| 12 | intmain() |
| 13 | { |
| 14 | signal(SIGALRM,func); |
| 15 | alarm(2); |
| 16 | while(1); |
| 17 | return0; |
| 18 | } |
| 19 |
Linux內(nèi)置的3個定時器
Linux為每個任務安排了3個內(nèi)部定時器:
ITIMER_REAL:實時定時器,不管進程在何種模式下運行(甚至在進程被掛起時),它總在計數(shù)。定時到達,向進程發(fā)送SIGALRM信號。
ITIMER_VIRTUAL:這個不是實時定時器,當進程在用戶模式(即程序執(zhí)行時)計算進程執(zhí)行的時間。定時到達后向該進程發(fā)送SIGVTALRM信號。
ITIMER_PROF:進程在用戶模式(即程序執(zhí)行時)和核心模式(即進程調(diào)度用時)均計數(shù)。定時到達產(chǎn)生SIGPROF信號。ITIMER_PROF記錄的時間比ITIMER_VIRTUAL多了進程調(diào)度所花的時間。
定時器在初始化是,被賦予一個初始值,隨時間遞減,遞減至0后發(fā)出信號,同時恢復初始值。在任務中,我們可以一種或者全部三種定時器,但同一時刻同一類型的定時器只能使用一個。
用到的函數(shù)有:
#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; /*當前時間計數(shù)*/
};
it_interval用來指定每隔多長時間執(zhí)行任務, it_value用來保存當前時間離執(zhí)行任務還有多長時間。比如說, 你指定it_interval為2秒(微秒為0),開始的時候我們把it_value的時間也設定為2秒(微秒為0),當過了一秒, it_value就減少一個為1, 再過1秒,則it_value又減少1,變?yōu)?,這個時候發(fā)出信號(告訴用戶時間到了,可以執(zhí)行任務了),并且系統(tǒng)自動把it_value的時間重置為it_interval的值,即2秒,再重新計數(shù)。
為了幫助你理解這個問題,我們來看一個例子:
| 1 | #include<stdio.h> |
| 2 | #include<signal.h> |
| 3 | #include<sys/time.h> |
| 4 | |
| 5 | /* |
| 6 | ******************************************************************************************************* |
| 7 | **Functionname:main() |
| 8 | **Descriptions:Demofortimer. |
| 9 | **Input:NONE |
| 10 | **Output:NONE |
| 11 | **Createdby:Chenxibing |
| 12 | **CreatedDate:2005-12-29 |
| 13 | **----------------------------------------------------------------------------------------------------- |
| 14 | **Modifiedby: |
| 15 | **ModifiedDate: |
| 16 | **----------------------------------------------------------------------------------------------------- |
| 17 | ******************************************************************************************************* |
| 18 | */ |
| 19 | intlimit=10; |
| 20 | /*signalprocess*/ |
| 21 | voidtimeout_info(intsigno) |
| 22 | { |
| 23 | if(limit==0) |
| 24 | { |
| 25 | printf("Sorry,timelimitreached. "); |
| 26 | return; |
| 27 | } |
| 28 | printf("only%dsencondsleft. ",limit--); |
| 29 | } |
| 30 | |
| 31 | /*initsigaction*/ |
| 32 | voidinit_sigaction(void) |
| 33 | { |
| 34 | structsigactionact; |
| 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 | voidinit_time(void) |
| 44 | { |
| 45 | structitimervalval; |
| 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 | intmain(void) |
| 55 | { |
| 56 | init_sigaction(); |
| 57 | init_time(); |
| 58 | printf("Youhaveonly10secondsforthinking. "); |
| 59 | |
| 60 | while(1); |
| 61 | return0; |
| 62 | } |
| 63 |
對于ITIMER_VIRTUAL和ITIMER_PROF的使用方法類似,當你在setitimer里面設置的定時器為ITIMER_VIRTUAL的時候,你把sigaction里面的SIGALRM改為SIGVTALARM, 同理,ITIMER_PROF對應SIGPROF。
不過,你可能會注意到,當你用ITIMER_VIRTUAL和ITIMER_PROF的時候,你拿一個秒表,你會發(fā)現(xiàn)程序輸出字符串的時間間隔會不止2秒,甚至5-6秒才會輸出一個,至于為什么,自己好好琢磨一下^_^
sleep
下面我們來看看用sleep以及usleep怎么實現(xiàn)定時執(zhí)行任務。
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
static char msg[] = "I received a msg.
";
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秒*/
/*向主進程發(fā)送信號,實際上是自己給自己發(fā)信號*/
sigqueue(getpid(), 50, tsval);
}
return 0;
}
看到了吧,這個要比上面的簡單多了,而且你用秒表測一下,時間很準,指定2秒到了就給你輸出一個字符串。所以,如果你只做一般的定時,到了時間去執(zhí)行一個任務,這種方法是最簡單的。
時間差
下面我們來看看,通過自己計算時間差的方法來定時:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
static char msg[] = "I received a msg.
";
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秒,則立刻發(fā)送信號*/
if (nowtime - lasttime >= 2)
{
/*向主進程發(fā)送信號,實際上是自己給自己發(fā)信號*/
sigqueue(getpid(), 50, tsval);
lasttime = nowtime;
}
}
return 0;
}
這個和上面不同之處在于,是自己手工計算時間差的,如果你想更精確的計算時間差,你可以把 time 函數(shù)換成gettimeofday,這個可以精確到微妙。
上面介紹的幾種定時方法各有千秋,在計時效率上、方法上和時間的精確度上也各有不同,采用哪種方法,就看你程序的需要。
https://blog.csdn.net/skc361/article/details/20544933
總結
以上是生活随笔為你收集整理的Linux定时器的使用(三种方法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用EclEmma 插件 解析jacoc
- 下一篇: 委托(Delegate)简介