1. setitimer函数
setitimer系统调用和alarm类似,也是用于设置一个定时器,该定时器超时会发送信号,还可以实现周期性的定时,即每隔一段时间发送一次信号,不过setitimer函数比alarm更加强大,时间精度可达到微秒us。
另外,linux为进程提供了三种定时器,每一种定时器超时都会给进程发送信号,但是每一种定时器发送的信号是不一样的,这点要注意区分。
函数原型:
#include <sys/time.h>
int setitimer(int which , const struct itimerval *new_value , struct itimerval *old_value);
返回值说明:成功返回0;失败则返回-1并设置errno
在调用setitimer创建定时器时,可通过指定参数which创建以下几种不同的定时器:
ITIMER_REAL:表示以linux系统的真实时间来计时的定时器,每次超时将会产生SIGALRM信号并发送给进程。
ITIMER_VIRTUAL:以进程在用户模式下执行的cpu时间来计时的定时器,每次超时将会产生SIGVTALRM信号并发送给进程
ITIMER_PROF:以进程在用户和内核两种模式下执行的cpu时间总和来计时的定时器,每次超时将会产生SIGPROF信号并发送给进程
参数new_value指定定时器超时时间,参数old_value返回上一次定时器的时间,另外new_value和old_value两个参数的类型都是struct itimerval类型,具体定义如下:
struct itimerval {
struct timeval it_interval; /* 周期性的定时器 */
struct timeval it_value; /* 一次性定时器 */
};
//itimerval结构体中的成员类型为timeval
struct timeval {
time_t tv_sec; /* 秒 */
suseconds_t tv_usec; /* 微秒 */
};
前面我们说过setitimer函数可以设置一次性的定时器,也可以设置周期性的定时器,简单来说itimerval用来指定哪一种定时器,而timeval用来指定定时器的超时时间单位。
也就是说,参数new_value中的it_interval成员表示指定为周期性的定时器,参数new_value中it_value成员表示指定为一次性的定时器 ,而timeval结构体中的tv_sec和tv_usec则表示定时器的超时时间是以秒为单位还是以微秒为单位。
举个栗子:
//指定一个一次性定时器,超时时间为1秒
itimerval it;
it.it_value.tv_sec = 1;
it.it_value.tv_usec = 0;
注意:如果it_interval的两个成员都为0则表示不指定周期性的定时器,也就是说以it_value指定一次性定时器
itimerval it;
it.it_interval.tv_sec = 0;
it.it_interval.tv_usec = 0;
2. 实验1——设置一次性定时器
实验以linux系统下的真实时间为例
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
int main(void)
{
//把time初始化,这一步操作不能少
struct itimerval time = {0};
//设置一次性定时器,超时时间为3秒
time.it_value.tv_usec = 0;
time.it_value.tv_sec = 3;
//如果不关心上一次定时器的超时时间,old_value可以传NULL
setitimer(ITIMER_REAL , &time , NULL);
//每隔1秒打印一次
while(1){
puts("hello world");
sleep(1);
}
return 0;
}
程序执行结果:
通过程序执行结果来看,setitimer程序每隔1秒打印一次hello world,当定时器超时后,发送了SIGALRM信号并终止了该进程。
3. 实验2——设置周期性定时器
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int signum){
if(signum == SIGALRM){
puts("hello SIGALRM");
}
}
int main(void)
{
//一定要把time初始化
struct itimerval time = {0};
//设置一次性定时器,超时时间为4秒
time.it_value.tv_usec = 0;
time.it_value.tv_sec = 4;
//设置周期性定时器,超时时间为3秒
//表示第一次超时为4秒,之后每次超时为3秒
time.it_interval.tv_sec = 3;
time.it_interval.tv_usec = 0;
signal(SIGALRM , sig_handler);
setitimer(ITIMER_REAL , &time , NULL);
while(1){
puts("hello world");
sleep(1);
}
return 0;
}
程序执行结果:
结果分析:第一次定时器为4秒,超时后发送了SIGALRM信号并打印hello SIGALRM。之后每次定时器都是3秒超时,然后发送SIGALRM信号。
注意:如果new_value中it_value的两个成员值都为0的话,那么将会屏蔽任何定时器
time.it_value.tv_usec = 0;
time.it_value.tv_sec = 0;
4. 实验3——自定义alarm函数
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <string.h>
unsigned int myalarm(unsigned int seconds)
{
struct itimerval old_time, itime;
//清空itime,这一步操作一定要做
memset(&itime , 0 , sizeof(itime));
itime.it_value.tv_sec = seconds;
setitimer(ITIMER_REAL,&itime,&old_time);
//printf("tv_sec=%ld\n",old_time.it_value.tv_sec);
return old_time.it_value.tv_sec;
}
int main()
{
unsigned int ret = 0;
//设置定时器的超时时间为10秒
ret = myalarm(10);
printf("old_time = %d\n",ret);
sleep(3);
//第二次修改了定时器的超时时间为3秒
ret = myalarm(3);
//获取到的上一次定时器时间
printf("old_time = %d\n",ret);
while(1){
printf("hello world\n");
sleep(1);
}
return 0;
}
执行结果:
根据程序执行结果来看,第一设置定时器的超时时间为10秒,定时器开始倒计时(tv_sec的值开始递减)
,程序休眠3秒后,第二次把定时器的超时时间修改为3秒,按理来说,此时上一次定时器的超时时间应该递减为7秒了,参数old_value获取上一次的定时器超时时间也是7。但程序打印出来的却是6秒,这是因为程序执行需要消耗时间,所以在获取上一次定时器的超时时间是有损耗的,真实时间应该是6秒多。
最后定时器在3秒后超时,向进程发送了SIGALRM信号。