《Linux系統調用:setitimer,getitimer,alarm》

一、介紹

setitimer()創建一個間隔式定時器(interval timer),會在未來某個點到期,並於此後每隔一段時間到期一次
getitimer()獲取定時器瞭解當前狀態、距離下次到期的剩餘時間
alarm()爲創建一次性實時定時器提供簡單的一個接口

二、接口函數

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
參數:
	seconds: 表示定時器到期的秒數,到期時會產生SIGALRM信號併發送給進程,因爲是實時和setitimer中的ITIMER_REAL一樣
	
	注意: alarm 和 settimer都是針對同一進程的共享實時定時器,也就是兩者改變都會影響對方
			  調用alarm()會覆蓋定時器前一個設置,調用alarm(0)會屏蔽現有定時器
			  alarm()返回值是定時器前一個設置距離到期的剩餘次數,如未設置定時器則返回0

返回值:
	alarm()返回的是剩餘定時器時間


#include <sys/time.h>

int setitimer(int which, const struct itimerval *new_value,
             struct itimerval *old_value);

參數:
	which: 指定不同類型的定時器
		ITIMER_REAL: 創建以真實時間倒計時的定時器,到期時會產生SIGALRM信號併發送給進程
		ITIMER_VIRTUAL:創建以進程虛擬時間(用戶態CPU時間)倒計時定時器,到期產生信號SIGVTALRM
		ITIMER_PROF:創建一個運行時計時定時器,以進程時間(用戶態+內核CPU時間的總和)倒計時,到期產生信號SIGPROF

	new_value:
		參數new_value下的it_value指定了距離定時器到期的延遲時間,it_interval則說明該定時器是否爲週期性定時器,
		如果it_interval兩個字段均爲0,那麼定時器就屬於在it_value所指定的時間間隔後到期的一次性定時器,
		只要it_interval中的任一字段非0,那麼每次定時器到期後,都會將定時器重置爲指定間隔後再次到期。
		
	  注意:進程只能擁有which指定的三種中的一種,所以如果第二次調用setitimer修改類型要符合which中的類型,如果將
		     new_value->it_value下的兩個字段都設置0,會屏蔽任何已有的定時器。
		   
	old_value:
		若不爲NULL,返回定時器前一設置。如果old_value->it_value兩個字段都爲0,那麼該定時器之前處於屏蔽狀態。
		如果 old_valuee->it_interval兩個字段爲0,那麼前一次是一次性定時器,如果不關心前一次設置那麼設置NULL

返回值:
	正確返回0,錯誤-1並設置errno			
			

int getitimer(int which, struct itimerval *curr_value);
參數:
	which: 參考setitimer
	curr_value: 得到which指定類型的定時器信息

返回值:
	正確返回0,錯誤-1並設置errno	


SUSv4廢止了UNIX API setitimer&getitimer,推薦使用POSIX定時器API

struct itimerval {
   struct timeval it_interval; /* Interval for periodic timer */
   struct timeval it_value;    /* Time until next expiration */
};

struct timeval {
   time_t      tv_sec;         /* seconds */
   suseconds_t tv_usec;        /* microseconds */
};

三、實例 setitimer

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include <malloc.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>


#ifdef  TRUE
#undef  TRUE
#endif
#define TRUE                     1

#ifdef  FALSE
#undef  FALSE
#endif
#define FALSE                    0

static volatile sig_atomic_t gotAlarm = 0;

void errExit(char * msg)
{
	printf("%s\n",msg);
	exit(EXIT_FAILURE);
}

void usageErr(const char *format, ...)
{
	va_list argList;

	fflush(stdout);           /* Flush any pending stdout */

	fprintf(stderr, "Usage: ");
	va_start(argList, format);
	vfprintf(stderr, format, argList);
	va_end(argList);

	fflush(stderr);           /* In case stderr is not line-buffered */
	exit(EXIT_FAILURE);
}

static void displayTimes(const char *msg, int includeTimer)
{
	struct itimerval itv;
	static struct timeval start;
	struct timeval curr;
	static int callNum = 0;             /* Number of calls to this function */

	if (callNum == 0)                    /* Initialize elapsed time meter */
	if (gettimeofday(&start, NULL) == -1)
		errExit("gettimeofday");

	if (callNum % 20 == 0)              /* Print header every 20 lines */
	printf(" Elapsed   Value Interval\n");

	if (gettimeofday(&curr, NULL) == -1)
		errExit("gettimeofday");                                                                                           
	printf("%-7s %6.2f", msg, curr.tv_sec - start.tv_sec +(curr.tv_usec - start.tv_usec) / 1000000.0);

	if (includeTimer) {
		if (getitimer(ITIMER_REAL, &itv) == -1)
			errExit("getitimer");
		printf("  %6.2f  %6.2f",itv.it_value.tv_sec + itv.it_value.tv_usec / 1000000.0,
						      itv.it_interval.tv_sec + itv.it_interval.tv_usec / 1000000.0);
	}

	printf("\n");
	callNum++;
}

static void sigalrmHandler(int sig)
{
	gotAlarm = 1;
}

int main(int argc, char *argv[])
{
	struct itimerval itv;
	clock_t prevClock;
	int maxSigs;               /* Number of signals to catch before exiting */                                            
	int sigCnt;                   /* Number of signals so far caught */
	struct sigaction sa;
	char *endptr;

	if (argc > 1 && strcmp(argv[1], "--help") == 0)
	usageErr("%s [secs [usecs [int-secs [int-usecs]]]]\n", argv[0]);

	sigCnt = 0;

	sigemptyset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_handler = sigalrmHandler;
	if (sigaction(SIGALRM, &sa, NULL) == -1)
		errExit("sigaction");

	/* Set timer from the command-line arguments */

	itv.it_value.tv_sec = (argc > 1) ? strtol(argv[1], &endptr, 10):0;
	itv.it_value.tv_usec = (argc > 2) ? strtol(argv[1], &endptr, 10):2;
	itv.it_interval.tv_sec = (argc > 3) ? strtol(argv[1], &endptr, 10):0;
	itv.it_interval.tv_usec = (argc > 4) ? strtol(argv[1], &endptr, 10):0;

	/* Exit after 3 signals, or on first signal if interval is 0 */

	maxSigs = (itv.it_interval.tv_sec == 0 && itv.it_interval.tv_usec == 0) ? 1 : 3;
                                                                                                         
	displayTimes("START:", FALSE);                                                                                         
	if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
		errExit("setitimer");

	prevClock = clock();
	sigCnt = 0;

	for (;;) {

		/* Inner loop consumes at least 0.5 seconds CPU time */

		while (((clock() - prevClock) * 10 / CLOCKS_PER_SEC) < 5) {
			if (gotAlarm) {                     /* Did we get a signal? */
				gotAlarm = 0;
				displayTimes("ALARM:", TRUE);

				sigCnt++;
				if (sigCnt >= maxSigs) {
					printf("That's all folks\n");
					exit(EXIT_SUCCESS);
				}
			}
		}

		prevClock = clock();
		displayTimes("Main: ", TRUE);
	}
}
yexiang@ubuntu:<_Sys>$ ./a.out 1 800000 1 0
 Elapsed   Value Interval
START:    0.00
Main:     0.50    0.50    1.00
ALARM:    1.00    1.00    1.00
Main:     1.00    1.00    1.00
Main:     1.50    0.50    1.00
ALARM:    2.00    1.00    1.00
Main:     2.00    1.00    1.00
Main:     2.50    0.50    1.00
ALARM:    3.00    1.00    1.00
That's all folks

四、實例alarm

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include <malloc.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>

static void sig_alrm(int signo)
{
	system("date");
	return;
}

int main(void)
{
	signal(SIGALRM,sig_alrm);
	system("date");
	alarm(20);
	sleep(5);
	printf("%d\n",alarm(5));
	pause();
}
// alarm(5) 的時候會清除之前的定時器並且返回之前的剩餘秒數,由於sleep了5秒,剩餘15秒
// 並且5秒後觸發信號,所以時間過去了10秒
yexiang@ubuntu:<_Sys>$ ./a.out 
Thu Jan 16 18:59:32 PST 2020
15
Thu Jan 16 18:59:42 PST 2020

// 如果 alarm(5) 改爲 alarm(0) 結果如下:
// 會一直等待,因爲alarm(0) 取消掉了定時器不會觸發信號
// pause又要等待信號執行才往下走,但等不到,所以一直等待
yexiang@ubuntu:<_Sys>$ ./a.out 
Thu Jan 16 19:02:12 PST 2020
15

 

發佈了428 篇原創文章 · 獲贊 147 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章