1. alarm() 函數
適用於精度要求不高的場景,比如幾秒。
函數原型:
unsigned int alarm(unsigned int seconds);
函數說明:
該種定時器方法是通過alarm()函數和signal()函數配合完成,alarm函數用來定時,當到達定時的時間後,內核會發送SIGALARM信號給進程,默認會結束進程。也可以通過signal函數爲信號SIGALRM設置處理函數,若參數seconds 爲0,則之前設置的鬧鐘會被取消,並將剩下的時間返回;
函數返回值:如果進程設置了鬧鐘時間,在調用alarm()之後,則返回上一個鬧鐘時間,否則返回0,出錯返回-1。
alarm()執行後,進程將繼續執行,在後期(alarm以後)的執行過程中將會在seconds秒後收到信號SIGALRM並執行其處理函數。
示例1:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h> //alarm的庫
int count;
void sigalrm_func(int sig){
count++;
printf("alarm![%d]\n",count);
alarm(1);
printf("over\n");
return;
}
int main(int argc,char *argv[])
{
signal(SIGALRM, sigalrm_func);
alarm(2);
printf("see\n");
while(1)
{
if(count == 3)
break;
}
}
程序先是等待定時2秒然後發送信號觸發調用函數,然後在調用函數每一秒再設置一次alarm觸發調用函數,結果如下:
示例2:
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
sigset_t block;
sigemptyset(&block);
sigaddset(&block, SIGALRM);
sigprocmask(SIG_BLOCK, &block, NULL);
while (1) {
printf("%ld\n", time(NULL));
alarm(3);
printf("see\n");
sigwaitinfo(&block, NULL);
}
return 0;
}
輸出結果:
其中幾個函數解釋:
sigwaitinfo()函數:
#include <signal.h>
int sigwaitinfo(sigset_t *set, siginfo_t *info)
阻塞一個進程直到特定信號發生,但信號到來時不執行信號處理函數,而是返回信號值。 調用該函數的典型代碼爲:
sigwaitinfo();
sigset_t newmask;
int rcvd_sig;
siginfo_t info;
sigemptyset(&newmask);
sigaddset(&newmask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &newmask, NULL);
rcvd_sig = sigwaitinfo(&newmask, &info);
if(rcvd_sig == -1)
{
//…...
}
其中:
1. siget_t 信號集定義爲一種數據類型,用來描述信號的集合:
typedef struct{
unsigned long sig[_NSIG_WORDS];
}sigset_t
2.int sigemptyset(sigset_t* set)
函數說明:用來將參數set信號集初始化並清空。
返回值:成功返回0,錯誤返回-1。
3.int sigaddset(sigset_t* set, int signum)
函數說明:用來將signum代表的信號加入至參數set信號集裏。
返回值:成功返回0,錯誤返回-1。
4.int sigpromask(int how, const sigset_t* set, sigset_t* oldset)
函數說明:用來改變目前的信號遮罩,其操作依參數how來決定。
返回值:成功返回0,錯誤返回-1。
5.void (signal(int signum, void( handler)(int)))(int);
函數說明: signal()會依據參數signum指定的信號編號來設置該信號的處理函數,當指定的信號到達時就會跳轉參數handler指定的函數執行。
返回值:返回先前的信號處理函數指針,如果有錯誤返回SIG_ERR(-1)
示例3:
不同情況的alarm的返回值
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
unsigned a;
void fun(int sig)
{
if(sig== SIGALRM)
printf("gettimer alarm!\n");
else
printf("gettimer failed!\n");
}
int main(int argc,char *argv[])
{
unsigned int a,b;
alarm(6);
sleep(2);
a = alarm(3);
printf("The rest time of the first alarm is %u s\n",a);
b = alarm(2);
signal(SIGALRM,fun);
int i = 0;
for(i=0;i<3;i++)
{
printf("%d\n",i);
sleep(1);
}
printf("The return valuable of the last alarm is %u s\nend\n",b);
return 0;
}
輸出結果:
2. setitimer()
setitimer()爲Linux的API,不同於C語言的StandardLibrary。setitimer()有兩個功能,
一是指定一段時間後,才執行某個function,類似alarm,但是精度更高;
二是每間隔一段時間就調用某個函數;
函數原型:
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
需要包含頭文件 #include <sys/time.h>
其中:
which: 指定定時器類型 ,setitimer 支持三種類型定時器:
ITIMER_REAL: 以系統真實的時間來計算,發送SIGALRM信號。
ITIMER_VIRTUAL: -以該進程在用戶態下花費的時間來計算,發送SIGVTALRM信號。
ITIMER_PROF: 以該進程在用戶態下和內核態下所費的時間來計算,發送SIGPROF信號。
new_value, old_value 皆爲itimerval 結構的實例:
struct itimerval
{
struct timerval it_interval; //指定間隔時間
struct timerval it_value; //指定初始定時時間
}
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微妙
};
itimeval由兩個timeval結構體組成,timeval包含tv_sec和tv_usec兩部分,其中tv_sec爲秒,tv_usec爲微秒(即1/1000000秒)
其中的new_value參數用來對計時器進行設置,it_interval爲計時間隔,it_value爲延時時長,下面例子中表示的是在setitimer方法調用成功後,延時1微秒便觸發一次SIGALRM信號,以後每隔200毫秒觸發一次SIGALRM信號。settimer工作機制是,先對it_value倒計時,當it_value爲零時觸發信號,然後重置爲it_interval,繼續對it_value倒計時,一直這樣循環下去。
基於此機制,setitimer既可以用來延時執行,也可定時執行。
假如it_value爲0是不會觸發信號的,所以要能觸發信號,it_value得大於0;如果it_interval爲零,只會延時,不會定時(也就是說只會觸發一次信號)。
old_value參數,通常用不上,設置爲NULL,它是用來存儲上一次setitimer調用時設置的new_value值。
示例1 :
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
int count = 0;
void set_timer()
{
struct itimerval itv;
memset(&itv,0,sizeof(itv));
itv.it_interval.tv_sec = 1;//每隔1秒
itv.it_interval.tv_usec = 0;
itv.it_value.tv_sec = 3;//第一次3秒
itv.it_value.tv_usec = 0;
int ret = setitimer(ITIMER_REAL, &itv, NULL);
if(ret)
{
printf("setitimer failed!/n");
}
}
void sigalrm_handler(int sig)
{
if(sig == SIGALRM)
{
count++;
printf("caught signal.. %d\n", count);
}
else
{
printf("caught signal failed!\n");
}
}
int main()
{
signal(SIGALRM, sigalrm_handler);
set_timer();
while (count < 5)
{}
exit(0);
}
輸出結果:
示例2: 阻塞的例子
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
int main(int argc, char *argv[])
{
sigset_t block;
struct itimerval itv;
sigemptyset(&block);
sigaddset(&block, SIGALRM);
sigprocmask(SIG_BLOCK, &block, NULL);
itv.it_interval.tv_sec = 2;
itv.it_interval.tv_usec = 0;
itv.it_value = itv.it_interval;
setitimer(ITIMER_REAL, &itv, NULL);
while (1)
{
printf("%ld\n", time(NULL));
printf("see\n");
sigwaitinfo(&block, NULL);
}
return 0;
}
輸出結果:
參考鏈接:
Linux下的定時器以及POSIX定時器:timer_settime()