歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux下定時器的使用及實現秒以下精確定時與休眠

Linux下定時器的使用及實現秒以下精確定時與休眠

日期:2017/2/28 16:14:24   编辑:Linux教程

Linux下定時器的使用 -- alarm() & setitimer():
1、alarm
-------------------------------------------
如果不要求很精確的話,用alarm()和signal()就夠了
unsigned int alarm(unsigned int seconds)
函數說明: alarm()用來設置信號SIGALRM在經過參數seconds指定的秒數後傳送給目前的進程。如果參數seconds為0,則之前設置的鬧鐘會被取消,並將剩下的時間返回。
返回值: 返回之前鬧鐘的剩余秒數,如果之前未設鬧鐘則返回0。
alarm()執行後,進程將繼續執行,在後期(alarm以後)的執行過程中將會在seconds秒後收到信號SIGALRM並執行其處理函數。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm_fn(int sig)
{
printf("alarm!\n");
alarm(2);
return;
}
int main(void)
{
signal(SIGALRM, sigalrm_fn);
alarm(1);
while(1) pause();
}
2、setitimer()
-------------------------------------------
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能強大,支持3種類型的定時器:
ITIMER_REAL : 以系統真實的時間來計算,它送出SIGALRM信號。
ITIMER_VIRTUAL : -以該進程在用戶態下花費的時間來計算,它送出SIGVTALRM信號。
ITIMER_PROF : 以該進程在用戶態下和內核態下所費的時間來計算,它送出SIGPROF信號。
setitimer()第一個參數which指定定時器類型(上面三種之一);第二個參數是結構itimerval的一個實例;第三個參數可不做處理。
setitimer()調用成功返回0,否則返回-1。

下面是關於setitimer調用的一個簡單示范,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM信號:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
int sec;
void sigroutine(int signo){
switch (signo){
case SIGALRM:
printf("Catch a signal -- SIGALRM \n");
signal(SIGALRM, sigroutine);
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM \n");
signal(SIGVTALRM, sigroutine);
break;
}
return;
}
int main()
{
struct itimerval value, ovalue, value2; //(1)
sec = 5;
printf("process id is %d\n", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue); //(2)
value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
for(;;)
;
}
(1) struct itimerval
struct itimerval {
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
itimerval: i --> interval
val --> value
itimerval結構中的it_value是減少的時間,當這個值為0的時候就發出相應的信號了. 然後再將it_value設置為it_interval值.
(2) setitimer()
setitimer()為其所在進程設置一個定時器,如果itimerval.it_interval不為0(it_interval的兩個域都不為0),則該定時器將持續有效(每隔一段時間就會發送一個信號)
注意:Linux信號機制基本上是從Unix系統中繼承過來的。早期Unix系統中的信號機制比較簡單和原始,後來在實踐中暴露出一些問題,因此,把那些建立在早期機制上的信號叫做"不可靠信號",信號值小於SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信號都是不可靠信號。這就是"不可靠信號"的來源。它的主要問題是:進程每次處理信號後,就將對信號的響應設置為默認動作。在某些情況下,將導致對信號的錯誤處理;因此,用戶如果不希望這樣的操作,那麼就要在信號處理函數結尾再一次調用signal(),重新安裝該信號。

***********************************

Linux下如何實現秒以下精確定時與休眠

Linux中提供的休眠函數是sleep和alarm,但是他們僅僅提供以秒為單位的休眠,這中休眠有些進程顯然太長了,那麼怎樣才能使進程以更小的時間分辨率休眠呢?
我知道的方法有2種,下面就做分別介紹。
第一種方法是使用定時器,Linux提供的定時器函數是:
int setitimer(int which, const struct itimerval *value, struct
itimerval *ovalue);
which指定那種定時器。Linux提供3種定時器:
TIMER_REAL: 准確定時器,超時會發出SIGALRM信號;
TIMER_VIRTUAL: 虛擬定時器,只記進程時間,所以會根據進程執行時間而變化,不能實現准確定時,超時發出SIGVTALRM信號;
TIMER_PROF: 梗概計時器,它會根據進程時間和系統時間而變化,不能實現准確定時,超時發出SIGPROF信號;
在進程中應該捕捉所設定時器會發出的信號,因為進程收到定時器超時發出的信號後,默認動作是終止。
value是設置定時器時間,相關結構如下:
struct itimerval {
struct timeval it_interval;
struct timeval it_value;
};
struct timeval {
long tv_sec;
long tv_usec;
};
it_interval指定間隔時間,it_value指定初始定時時間。如果只指定it_value,就是實現一次定時;如果同時指定 it_interval,則超時後,系統會重新初始化it_value為it_interval,實現重復定時;兩者都清零,則會清除定時器。
tv_sec提供秒級精度,tv_usec提供微秒級精度,以值大的為先,注意1s = 1000000ms。
ovalue用來保存先前的值,常設為NULL。
如果是以setitimer提供的定時器來休眠,只需阻塞等待定時器信號就可以了。
第二種方法是使用select來提供精確定時和休眠:
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
n指監視的文件描述符范圍,通常設為所要select的fd+1,readfds,writefds和exceptfds分別是讀,寫和異常文件描述符集,timeout為超時時間。
可能用到的關於文件描述符集操作的宏有:
FD_CLR(int fd, fd_set *set); 清除fd
FD_ISSET(int fd, fd_set *set); 測試fd是否設置
FD_SET(int fd, fd_set *set); 設置fd
FD_ZERO(fd_set *set); 清空描述符集
我們此時用不到這些宏,因為我們並不關心文件描述符的狀態,我們關心的是select超時。所以我們需要把readfds,writefds和exceptfds都設為NULL,只指定timeout時間就行了。至於n我們可以不關心,所以你可以把它設為任何非負值。實現代碼如下:

int msSleep(long ms) {

struct timeval tv;

tv.tv_sec = 0;

tv.tv_usec = ms;

return select(0, NULL, NULL, NULL, &tv);

}

呵呵,怎麼樣,是不是很簡單?
結語:
setitimer和select都能實現進程的精確休眠,本文分別對他們進行了簡單介紹,並給出了一個簡單的給予select的實現。我不推薦使用 setitimer,因為一者Linux系統提供的timer有限(每個進程至多能設3個不同類型的timer),再者ssetitimer實現起來沒有 select簡單。

Copyright © Linux教程網 All Rights Reserved