Unix/Linux编程:getitimer、setitimer、alarm

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);         //关闭闹钟
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值