《Linux系统调用:setitimer,getitimer,alarm》

流过昼夜 提交于 2020-02-07 09:07:39

一、介绍

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

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!