1、Linux定时器的使用(信号)
1.1 Alarm
alarm用在不需要经确定时的时候,返回之前剩余的秒数
它可以在进程中设置一个定时器,当定时器指定的时间到时,它向进程发送SIGALRM信号。如果忽略或者不捕获此信号,则其默认动作是终止调用该alarm函数的进程。
要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。需要注意的是,经过指定的秒数后,信号由内核产生,由于进程调度的延迟,所以进程得到控制从而能够处理该信号还需要一些时间。
如果有以前为进程登记的尚未超时的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值。
#include<unistd.h>
成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
出错:-1
Ep1:
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <signal.h>
void func(int n)
{
printf("2 s reached.\n");
}
int main()
{
signal(SIGALRM, func); //不捕获该信号时进程终止
//当定时器指定的时间到时,它向进程发送SIGALRM信号。
//如果忽略或者不捕获此信号,则其默认动作是终止调用该alarm函数的进程
alarm(2);
while (1);
return 0;
}
Ep2:
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int ret = alarm(5);
printf("%d\n", ret);//则返回上一个闹钟时间的剩余时间
sleep(3);
ret = alarm(10);//以前的闹钟时间2都被新值10所代替
printf("%d\n", ret);//则返回上一个闹钟时间的剩余时间
//(10后受到闹钟发出的SIGALRM),程序结束
pause();//令目前的程序暂停(进入休眠状态,直到被信号(signal)所中断
return 0;
}
1.2 linux内置定时器- setitimer
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
which为定时器类型,setitimer支持3种类型的定时器:
ITIMER_REAL: 以系统真实的时间来计算(不管进程在何种模式下运行(甚至在进程被挂起时),它总在计数),它送出SIGALRM信号。
ITIMER_VIRTUAL: -以该进程在用户态(即程序执行时)下花费的时间来计算(计算进程执行的时间),它送出SIGVTALRM信号。
ITIMER_PROF: 以该进程在用户态下(即程序执行时)和内核态下(即进程调度用时)所费的时间来计算,它送出SIGPROF信号, ITIMER_PROF记录的时间比ITIMER_VIRTUAL多了进程调度所花的时间。
setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。
setitimer()调用成功返回0,否则返回-1
参数介绍
/* Type of the second argument to `getitimer' and
the second and third arguments `setitimer'. */
struct itimerval
{
/* Value to put into `it_value' when the timer expires. */
struct timeval it_interval; //则超时后,系统会重新初始化it_value为it_interval,实现重复定时
/* Time to the next timer expiration. */
struct timeval it_value;//指定初始定时时间
};
it_interval指定间隔时间,it_value指定初始定时时间。如果只指定it_value,就是实现一次定时;如果同时指定 it_interval,则超时后,系统会重新初始化it_value为it_interval,实现重复定时;两者都清零,则会清除定时器。
/* A time value that is accurate to the nearest
microsecond but also has a range of years. */
struct timeval
{
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};
tv_sec提供秒级精度,tv_usec提供微秒级精度,以值大的为先,注意1s = 1000 000us
ovalue用来保存先前的值,常设为NULL
实例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
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;
}
fflush(stdout);
return;
}
//在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号
int main()
{
struct itimerval value, ovalue, value2; //(1)
printf("process id is %d\n", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);
//setting it_value is one seconds
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
//setting it_interval value is one seconds
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
//每隔一秒发送一次送出SIGALRM信号(以系统真实的时间来计算)
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;
//每隔0.5秒发出一个SIGVTALRM信号
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
while(true);
}
1.3 sleep,usleep实现定时器
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
static char msg[] = "I received a msg.\n";
int len;
void show_msg(int signo)
{
write(STDERR_FILENO, msg, len);
}
int main()
{
struct sigaction act;
union sigval tsval;
act.sa_handler = show_msg;//代表新的信号处理函数
act.sa_flags = 0;
sigemptyset(&act.sa_mask);//清空此信号集
//参数signum = 50 可以指定SIGKILL和SIGSTOP以外的所有信号
sigaction(50, &act, NULL);
len = strlen(msg);
while (1)
{
sleep(2); /*睡眠2秒*/
/*向主进程发送信号,实际上是自己给自己发信号*/
sigqueue(getpid(), 50, tsval);//sigqueue()发送信号
}
return 0;
}
1.4 时间差
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
static char msg[] = "I received a msg.\n";
int len;
static time_t lasttime;
void show_msg(int signo)
{
write(STDERR_FILENO, msg, len);
}
int main()
{
struct sigaction act;
union sigval tsval;
act.sa_handler = show_msg;//代表新的信号处理函数
act.sa_flags = 0;
sigemptyset(&act.sa_mask);//清空此信号集
//参数signum = 50 可以指定SIGKILL和SIGSTOP以外的所有信号
sigaction(50, &act, NULL);
len = strlen(msg);
time(&lasttime);
while (1)
{
time_t nowtime;
/*获取当前时间*/
time(&nowtime);
/*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/
if (nowtime - lasttime >= 2)
{
/*向主进程发送信号,实际上是自己给自己发信号*/
sigqueue(getpid(), 50, tsval);//sigqueue()发送信号
lasttime = nowtime;
}
}
return 0;
}
注:使用CPU时间替换系统时间计算时间差更精确