22-用setitimer函数实现计时器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35733751/article/details/82763230

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信号。

猜你喜欢

转载自blog.csdn.net/qq_35733751/article/details/82763230
今日推荐