900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Linux 定时器 setitimer

Linux 定时器 setitimer

时间:2021-12-27 20:25:47

相关推荐

Linux 定时器 setitimer

阅读了《Unix/Linux系统编程》中关于定时器及时钟服务的部分,结合网上资料进行了整理

1. 相关概念

基于X86架构的个人计算机有数个定时器,包括实时时钟RTC、可编程间隔定时器PIT、多核CPU中的本地定时器、高分辨率定时器。

实时时钟RTCRTC由一个小型备用电池供电,即使计算机关机时,它也能够连续运行。RTC用于实时提供时间和日期信息。由于RTC在电脑关机时继续运行,因此可以解释为什么开机后系统显示的时间与现实中保持一致。时间变量是一个长整数,即从1970年1月1日起经过的秒数

2. 时钟相关系统调用与库函数

2.1 gettimeofday系统调用

返回当前时间即当前秒数和微秒数。其中秒数是相对于1970年1月1日0点所经过的秒数。

int gettimeofday(struct timeval * tv, struct timezone * tz)

参数tv指向一个timeval结构体变量,该变量保存返回的时间结果。

timeval结构体定义如下:tv_sec成员保存秒数,tv_usec成员保存微秒数

struct timeval{__time_t tv_sec;/* Seconds. */__suseconds_t tv_usec;/* Microseconds. */};

第二个参数类型timezone已过期,使用NULL即可。

2.2 settimeofday系统调用

设置系统时间

int settimeofday(const struct timeval *tv, const struct timezone *tz)

第一个参数tv即为要设置的系统时间第二个参数类型timezone已过期,使用NULL即可

2.3 ctime库函数

以日历形式显示当前日期和时间。

char * ctime(const time_t * timer)

参数是指向 time_t 变量的指针,该变量即为从1970年开始的秒数。返回值是一个C字符串,该字符串以日历形式表示当前日期和时间。

示例:获取当前时间并以日历形式显示

#include <stdio.h>#include <stdlib.h>#include <sys/time.h>#include <time.h>int main(){struct timeval t;gettimeofday(&t,NULL);//获取当前系统时间printf("cur time is : %s",ctime(&t.tv_sec));//以日历形式打印当前时间}

运行结果

xtark@xtark-vmpc:~/桌面/linux_study/section5$ gcc test.c xtark@xtark-vmpc:~/桌面/linux_study/section5$ ./a.out cur time is : Sat Jun 19 10:38:22

2.4 time系统调用

以秒为单位返回当前时间(从1970年到现在的秒数)。该系统调用缺点是时间精度是秒而不是微秒

time_t time(time_t * timer)

参数timer:如果该参数不是NULL,除了返回当前时间外,还将当前时间存储在timer中返回值:当前时间

2.5 clock函数

返回程序执行起(一般为程序的开头)到调用该函数时,占用CPU的时间(时钟计时单元)

clock_t clock(void)

返回值是自程序启动起,处理器时钟所使用的时间。如果失败,则返回 -1 值。

在time.h文件中,还定义了一个常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元

#define CLOCKS_PER_SEC ((clock_t) 1000000)

在Linux系统中,CLOCKS_PER_SEC表示的值可能不同。这里该宏定义表示1000000,因此每个时钟计时单元表示一微秒,即程序执行一微妙,clock函数的返回值就+1。

因此,为了获取 CPU 所使用的秒数,需要除以 CLOCKS_PER_SEC。

clock_t c = clock();//获取该程序从开始到现在占用CPU的时间(时钟计时单元)printf("%f\n",(double)c/CLOCKS_PER_SEC);//打印出占用CPU的秒数

需要注意,clock返回的是占用CPU的时间,因此如果进程不是处于运行态(即不占用CPU时),那么该时间不算在clock返回值内

示例:进程sleep 5秒,然后打印占用CPU时间

#include <stdio.h>#include <stdlib.h>#include <sys/time.h>#include <unistd.h>#include <time.h>int main(){sleep(5);clock_t c = clock();printf("%f\n",(double)c/CLOCKS_PER_SEC);}

运行结果:可以看出该进程占用CPU时间仅有0.000618秒,即sleep的时间并不算在clock函数返回值内,因为进程sleep时不占用CPU。

xtark@xtark-vmpc:~/桌面/linux_study/section5$ gcc test.c xtark@xtark-vmpc:~/桌面/linux_study/section5$ ./a.out 0.000618

2.6 times系统调用

参考资料

用于获取进程的具体执行时间,将结果保存在tms类型变量中

clock_t times(struct tms * buffer)

参数buffer:保存进程具体执行时间,tms结构体类型:

struct tms{clock_t tms_utime; /* User CPU time. 进程在用户模式下花费 CPU 时间*/clock_t tms_stime; /* System CPU time. 进程在系统模式(内核)下花费 CPU 时间 */clock_t tms_cutime; /* User CPU time of dead children. 已死掉子进程在用户模式下花费 CPU 时间*/clock_t tms_cstime; /* System CPU time of dead children. 已死掉子进程在系统(内核)模式下花费 CPU 时间*/};

返回值:返回自过去某个任意时间点以来所经过的时钟刻度的数量。 返回值可能溢出clock_t类型的时钟数。 出错时,返回-1,并适当设置 errno。

2.7 间隔定时器

参考资料

Linux为每个进程提供了三种不同类型的间隔计时器,可用作进程计时。

通过setitimer系统调用创建间隔定时器

2.7.1 setitimer系统调用

创建间隔定时器。通过which参数指定不同种类的定时器,当间隔定时器到期时,会向进程发送一个信号(即为发送给进程进行处理的一个数字1-31),并将定时器重置为指定的间隔值

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

参数which:间隔定时器类型。总共有三种定时器类型

ITIMER_REAL实时定时器,以系统真实的时间来计算,到期时它发出SIGALRM(14)信号。

ITIMER_VIRTUAL以该进程在用户态下花费的CPU时间来计算,它发出SIGVTALRM(26)信号。

ITIMER_PROF以该进程在用户态下和内核态下所费的CPU时间来计算。它发出SIGPROF(27)信号。

参数new_value:指向itimerval结构体变量,用来对计时器进行设置。

struct itimerval{/* 定时器过期时放入'it_value'的值(定时器过期时重置的下一次间隔时长)。 */struct timeval it_interval;/* 当前时刻距离定时器下一次到期的时长(初始定时时间)。 */struct timeval it_value;};

其中timeval结构体:

struct timeval{__time_t tv_sec;/* 秒 */__suseconds_t tv_usec;/* 微秒 */};

setitimer工作机制是,先对it_value倒计时,当it_value为零时触发信号。然后重置为it_interval。继续对it_value倒计时。一直这样循环下去。

如果 new_value.it_value 中的任一字段不为零,则定时器在指定时间初始到期。 如果 new_value.it_value 中的两个字段都为零,则定时器被解除。因此使用setitimer系统调用创建定时器时new_value.it_value中的两个字段不能都是0,不然就解除定时器了。

如果只指定it_value,即it_interval为零it_value大于0,仅仅会延时而不会定时(也就是说仅仅会触发一次信号)。

参数old_value:指向itimerval结构体变量。通常使用不上,即设置为NULL。用来存储上一次setitimer调用时设置的new_value值。

2.7.2 getitimer系统调用

函数getitimer()将由which指定的定时器的当前值放入cur_value指向的变量。

int getitimer(int __which, struct itimerval *cur_value)

cur_value.it_value是距离下一次到期还有多少时间。 这个值随着定时器的倒计时而变化,并在定时器到期时被重置为 it_interval。 如果it_value的两个字段都是零,那么这个定时器目前是被解除的(不活动)。

cur_value.it_interval为定时器的间隔值。如果it_interval的两个字段都是0,那么这是一个单次定时器(也就是说,它只延时一次,而不会定时间隔)。

2.7.3 间隔定时器示例

创建实时定时器

#include <stdio.h>#include <signal.h>#include <sys/time.h>#include <time.h>//信号处理函数void func(int sig){printf("signal : %d\n",sig);}int main(){signal(SIGALRM, func);//注册信号处理函数struct itimerval new_value;new_value.it_value.tv_sec = 1;//初始延时1秒new_value.it_value.tv_usec = 0;new_value.it_interval.tv_sec = 2;//定时器间隔2秒new_value.it_interval.tv_usec = 0;setitimer(ITIMER_REAL, &new_value, NULL);//创建定时器while(1);}

需要注意ITIMER_REAL定时器无论进程是否正在执行都进行定时。而另外两种定时器ITIMER_VIRTUAL和ITIMER_PROF仅在占用CPU时才进行定时(即进程处于运行态时才计时)。如下面的例子:

创建ITIMER_PROF定时器,并将进程sleep 5秒后进入while(1)死循环

#include <stdio.h>#include <signal.h>#include <sys/time.h>#include <time.h>#include <unistd.h>void func(int sig){printf("signal : %d\n",sig);}int main(int argc, char *argv[]){signal(SIGPROF, func);struct itimerval new_value;new_value.it_value.tv_sec = 0;new_value.it_value.tv_usec = 1;//初始延时1微秒new_value.it_interval.tv_sec = 0;new_value.it_interval.tv_usec = 500000;//定时器间隔0.5sprintf("begin timer\n");setitimer(ITIMER_PROF, &new_value, NULL);sleep(5);//sleep 5秒printf("5 seconds left \n");while(1);}

运行结果:可见进程在sleep时由于不占用CPU所以不会使得ITIMER_PROF计时

xtark@xtark-vmpc:~/桌面/linux_study/section5$ gcc test.c xtark@xtark-vmpc:~/桌面/linux_study/section5$ ./a.out begin timer5 seconds left signal : 27signal : 27signal : 27signal : 27signal : 27

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。