Linux C定時器使用指南


使用定時器的目的是為了周期性的執行某一任務,或者是到了某個指定時間去執行某一任務。要達到這一目的,一般有兩個常見的方法。一個是用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這個字符串。

11565339943_b893649db3_o.png

ITIMER_VIRTUAL和ITIMER_PROF的使用方法類似,當你在setitimer里面設置的定時器為 ITIMER_VIRTUAL的時候,你把sigaction里面的SIGALRM改為SIGVTALARM, 同理,ITIMER_PROF對應SIGPROF。

不過,你可能會注意到,當你用ITIMER_VIRTUAL和ITIMER_PROF的時候,你拿一個秒表,你會發現程序輸出字符串的時間間隔會不止2秒,甚至5-6秒才會輸出一個,至於為什么,自己好好琢磨一下。

如果函數init_sigaction報錯:

11565348733_cc934166da_o.png

解決方法:在文件最開頭加上如下代碼(在所有#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;}

11565244494_f62333c6ca_o.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM