getitimer、setitimer
SUSv4 废止了 getitimer()和 setitimer(),同时推荐使用 POSIX 定时器 API
系统调用setitimer()创建一个间隔式定时器(interval timer),这种定时器会在未来某个时间点到期,并于此后(可选择的)每隔一段段时间到期一次
NAME
getitimer, setitimer - get or set value of an interval timer
SYNOPSIS
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
通过在调用 setitimer()时为 which 指定以下值,进程可以创建 3 种不同类型的定时器
值 | 描述 |
---|---|
ITIMER_REAL | 创建以真实时间倒计时的定时器。到期时会产生 SIGALARM 信号并发送给进程。 |
ITIMER_VIRTUAL | 创建以进程虚拟时间(用户模式下的 CPU 时间)倒计时的定时器。到期时会产生信号SIGVTALRM。 |
ITIMER_PROF | 创建一个 profiling 定时器,以进程时间(用户态与内核态 CPU 时间的总和)倒计时。到期时,则会产生 SIGPROF 信号 |
对所有这些信号的默认处置(disposition)均会终止进程。除非真地期望如此,否则就需要针对这些定时器信号创建处理器函数
参数 new_value 和 old_value 均为指向结构 itimerval 的指针,结构的定义如下:
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
参数new_value的下属结构it_value指定了距离定时器到期的延时时间;it_interval则说明该定时器是否为周期性定时器。如果it_interval的两个字段值均为0,那么该定时器就属于在it_value所指定的时间间隔后到期的一次性定时器。只要it_interval中的任何一个值为非0,那么在定时器到期之后,都会将定时器重置为在指定间隔后再次到期。
进程只能拥有上述 3 种定时器中的一种。当第 2 次调用 setitimer()时,修改已有定时器的属性要符合参数 which 中的类型。如果调用 setitimer()时将new_value.it_value 的两个字段均置为 0,那么会屏蔽任何已有的定时器。
若参数 old_value 不为 NULL,则以其所指向的 itimerval 结构来返回定时器的前一设置。如果 old_value.it_value 的两个字段值均为 0,那么该定时器之前处于屏蔽状态。如果old_value.it_interval 的两个字段值均为 0,那么该定时器之前被设置为历经 old_value.it_value 指定时间而到期的一次性定时器。对于需要在新定时器到期后将其还原的情况而言,获取定时器的前一设置就很重要。如果不关心定时器的前一设置,可以将 old_value 置为 NULL。
定时器会从初始值(it_value)倒计时一直到 0 为止。递减为 0 时,会将相应信号发送给进程,随后,如果时间间隔值(it_interval)非 0,那么会再次将 it_value 加载至定时器,重新开始向 0 倒计时。
可以在任何时刻调用 getitimer(),以了解定时器的当前状态、距离下次到期的剩余时间。
系统调用 getitimer()返回由 which 指定定时器的当前状态,并置于由 curr_value 所指向的缓冲区中。这与 setitimer()借参数 old_value 所返回的信息完全相同,区别则在于 getitimer()无需为了获取这些信息而改变定时器的设置。子结构curr_value.it_value 返回距离下一次到期所剩余的总时间。该值会随定时器倒计时而变化,如果设置定时器时将 it_interval 置为非 0 值,那么会在定时器到期时将其重置。子结构 curr_value.it_interval 返回定时器的间隔时间,除非再次调用setitimer(),否则该值一直保持不变。
使用 setitimer()(和 alam(),稍后讨论)创建的定时器可以跨越 exec()调用而得以保存,但由 fork()创建的子进程并不继承该定时器。
alarm
alarm也称为闹钟函数,参数 seconds 表示定时器到期的秒数。到期时,会向调用进程发送 SIGALRM 信号。可以设置忽略或者不捕获此信号,如果采用默认方式其动作是终止调用该alarm函数的进程。
#include<unistd.h>
/*
* 参数: seconds:指定秒数
* 返回值:
* 成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
* 出错:-1
*/
unsigned int alarm(unsigned int seconds);
- 要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
- 需要注意的是,经过指定的秒数后,信号由内核产生,由于进程调度的延迟,所以进程得到控制从而能够处理该信号还需要一些时间。
调用 alarm()会覆盖对定时器的前一个设置。调用 alarm(0)可屏蔽现有定时器。
alarm()的返回值是定时器前一设置距离到期的剩余秒数,如未设置定时器则返回 0。
使用
示例1:
该示例完成了一个简单的sleep函数的功能,由于SIGALRM默认的系统动作为终止该进程,因此在程序调用pause之后,程序就终止了。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void){
alarm(30);
int ret;
sleep(10);
ret=alarm(10);
printf("%d\n",ret);
pause();
return 0;
}
示例2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int num)
{
printf("receive the signal %d.\n", num);
}
int main()
{
signal(SIGALRM, sig_handler); //SIGALRM是在定时器终止时发送给进程的信号
alarm(2);
pause();//pause()函数使该进程暂停让出CPU
exit(0);
}
运行结果:两秒钟后输出
示例3
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sig_handler(int num)
{
printf("receive the signal %d.\n", num);
alarm(2);
}
int main()
{
signal(SIGALRM, sig_handler);
alarm(2);
while(1)//做一个死循环,防止主线程提早退出,相等于线程中的join
{
pause();
}
//pause();//如果没有做一个死循环则只会让出一次cpu然后就还给主线程,主线程一旦运行结束就会退出程序
exit(0);
}
运行结果:每隔2秒钟就会输出一次。
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <bits/signum.h>
void sig_handler (int sig) {
if (sig == SIGALRM) {
puts ("+1s...");
alarm (1); // 重新设置1秒定时器
}
}
int main() {
struct sigaction sa;
bzero (&sa, sizeof (sa) );
sa.sa_handler = sig_handler;
assert (sigaction (SIGALRM, &sa, NULL) != -1);
alarm(1);
while (1) {
sleep (1);
}
return 0;
}
套接字IO超时设置
程序大概框架如下所示,如果read在5s内被SIGALRM信号中断而返回,则表示超时,否则未超时已读取到数据则取消闹钟。为了在超时时中断read函数,可以用信号处理函数来捕捉SIGALRM信号。
void handler(int sig)
{
return 0; //只是用来中断read函数,不需要进行处理
}
signal(SIGALRM,handler);
alarm(5); //开启闹钟
int ret =read(fd,buf,sizeof(buf));
if(ret==-1 && errno == EINTR) //read被超时中断
{
errno = ETIMEDOUT;
}
else if (ret>0) //读到数据
{
alarm(0); //关闭闹钟
}