linux定时器使用
Posted 无痕幽雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux定时器使用相关的知识,希望对你有一定的参考价值。
我们常常有设置系统在某一时间执行相应动作的需求,比如设置电脑什么时候自动锁屏,什么时候自动关机,设置应用程序什么时候自动运行,什么时候自动退出。这些与时间相关的功能,都需要依靠操作系统中的定时器来实现。
linux中定时器的使用原理很简单,你只需设置一个超时时间和相应的执行函数,系统就会在超时的时候执行一开始设置的函数。超时的概念有点模糊,它指的是你设定一个时间,如果当前的时间超过了你设定的时间,那就超时了。比如说,你设置五分钟后系统自动关机,系统会记住五分钟后的具体时间,也就是当前时间再加上五分钟。过了五分钟后,系统当前时间已经比刚才设置的具体时间大了,出现超时,于是运行超时行为。
在linux中,使用alarm函数可以设置一个定时器,当定时器超时的时候,会产生SIGALRM信号。因此,要设置超时行为,就得在SIGALRM信号上设置相应的函数。
包含头文件:#include <unistd.h>
函数原型:unsigned int alarm(unsigned int seconds);
具体例子如下:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void SigFun(int signo){
printf("SigFun is running\\n");
}
int main(){
if(signal(SIGALRM, SigFun) == SIG_ERR){
perror("signal\\n");
return -1;
}
alarm(5);
pause();
}
在linux中,一个进程只能有一个定时器,因此当一个进程需要设置多个定时行为时,需要采取相应的措施,使得一个定时器可以实现多个定时。主要的方法有两种,一种叫时间链,一种叫时间堆。
时间链是将所有定时行为以链表的方式连在一起,同时在进程中维护一个固定时间超时的定时器。当定时器超时的时候,检查链表上所有的行为是否超时,如果有超时的行为,则运行其行为,并将其从链表中删除。这用方法最大的坏处就是需要固定时间遍历整个链表,造成了比较大的开销。
时间堆是将所有定时行为以最小堆的方式组织起来,并且在进程中维护一个以堆顶为超时时间的定时器。当定时器超时时,检查堆顶行为是否超时,如果超时,则运行该行为,并将其从堆顶删除,接着继续检查;如果堆顶行为未超时,则用其超时时间继续设置定时器。时间堆不用固定时间去检查所有定时行为,只需在超时的时候运行相应的超时行为,效率比时间链高。
在linux中,alarm函数是以秒计时的,当有时我们需要更小的时间单位去设置行为,比如毫秒,应该怎么办呢?linux提供setitimer函数,可以提供更精确的定时器。
包含头文件:#include <sys/time.h>
函数原型:int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
参数:
int which有三种选择:
ITIMER_REAL:decrements in real time, and deliversSIGALRM upon expiration.
ITIMER_VIRTUAL:decrements only when the process is executing, anddeliversSIGVTALRM upon expiration.
ITIMER_PROF:decrements both when the process executes and when the system is executing on behalf of the process. Coupledwith ITIMER_VIRTUAL, this timer is usually used to profile the time spent by the application in user and kernel space. SIGPROF is delivered
其中,which为定时器类型,3中类型定时器如下:
ITIMER_REAL : 以系统真实的时间来计算,它送出SIGALRM信号。
ITIMER_VIRTUAL : -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF : 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。
第二个参数指定间隔时间,第三个参数用来返回上一次定时器的间隔时间,如果不关心该值可设为NULL。
it_interval指定间隔时间,it_value指定初始定时时间。如果只指定it_value,就是实现一次定时;如果同时指定 it_interval,则超时后,系统会重新初始化it_value为it_interval,实现重复定时;两者都清零,则会清除定时器。
tv_sec提供秒级精度,tv_usec提供微秒级精度,以值大的为先,注意1s = 1000000us。
如果是以setitimer提供的定时器来休眠,只需阻塞等待定时器信号就可以了。
setitimer()调用成功返回0,否则返回-1。
const struct itimerval *new_value的数据结构如下:
struct itimerval {
struct timeval it_interval; /* 第一次超时以后每次超时的时间 */
struct timeval it_value; /* 第一次超时的时间 */
};
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
struct itimerval *old_value是原来设置的时间,如果不需要用到,可以用NULL
成功调用返回0,失败返回-1。
具体例子如下:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
void SigFun(int signo){
printf("SigFun is running\\n");
}
int main(){
if(signal(SIGALRM, SigFun) == SIG_ERR){
perror("signal\\n");
return -1;
}
struct itimerval tv;
tv.it_value.tv_usec = 500000;
tv.it_value.tv_sec = 0;
tv.it_interval.tv_usec = 300000;
tv.it_interval.tv_sec = 0;
if(setitimer(ITIMER_REAL, &tv, NULL) != 0){
perror("setitimer\\n");
return -1;
}
while(true){
pause();
}
}
以上是关于linux定时器使用的主要内容,如果未能解决你的问题,请参考以下文章