Linux定时器2

1. POSIX定义的定时器

有了上一篇关于setitimer定时器的介绍,相信Linux下定时器的用法已经有了基本了解。我个人在使用setitimer定时器的时候,有一个非常头疼的问题,就是setitimer定时器不能设置多个严格意义上讲,setitimer定时器只能按照时间处理的类别,分别设置三种定时器,即ITIMER_REAL,ITIMER_VIRTUAL和ITIMER_PROF三类,具体解释见上一篇博客。但这非常限制Linux下定时器的使用。解决方法,就是用POSIX下定义的定时器。

可以把POSIX理解为一套新的标准,在这个标准下定义了新的定时器函数timer_create,这个函数支持创建同类型的多个定时器。

POSIX下的定时器划分为三个阶段,主要是三个函数的使用:

创建新的定时器,并定义其到期时对进程的通知方法timer_create()

启动或停止一个定时器timer_settime()

删除一个定时器timer_delete()

2. timer_create()

timer_create()函数的作用是创建一个定时器,并定义其到期时对进程的通知方法。如果创建成功,返回0, 如果创建失败,返回-1。函数的具体参数如下:

#define _POSIX_C_SOURCE 199309 // 源代码是符合 POSIX规范的
#include 
#include 

int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid);
return 0 on success, or -1 on error

这三个参数的理解都很重要,第一个参数为clockid,为设定的定时器使用的时间,在setitimer()中是三种时间,而POSIX下的timer_create()可以使用四种时间,具体如下表。当然,通常情况下我们会使用实时时间CLOCK_REALTIME。

Linux定时器2_第1张图片

第二个参数是一个sigevent类型的指针evp,evp的具体结构体的定义比较复杂,就不展开介绍了。但是功能上,evp决定了定时器到期时对程序的通知方式。与settimer函数比较的话,evp不仅可以通过信号的方式通知程序定时器到期,还有其他的方法。这里需要熟悉evp的两个成员变量,是evp结构体中定义的变量,sigev_notify,表示通知程序的方式,如果该变量的值为SIGEV_SIGNAL,则表示用信号通知。当然可以使用其他字段,如下图所示。

Linux定时器2_第2张图片

sigev_signo,表示如果使用信号通知,那么该信号的名称。这个变量的赋值是任意的,这也就意味着,我们可以设定同类型的多个定时器,用sigev_signo变量进行区分,分别对应不同的信号处理函数。这就解决了使用setitimer定时器不能设定多个定时器的问题。

第三个参数为timer_t类型的指针 timerid,其实这个参数也是为了设定多个定时器准备的,可以理解为每个定时器id,在后面启动,停止或删除某个定时器时,起到句柄的作用。具体使用看后面实例就很容易明白。

3. 启动或停止定时器timer_settime()

设定好的定时器,需要经过timer_settime()函数进行启动或停止,这个函数有四个参数。

#define _POSIX_C_SOURCE 199309
#include 
int timer_settime(time_t timerid, int flags, const struct itimerspec *value, struct itimerspec *old_value);
return 0 on success, or -1 on error

第一个参数用来定位具体的定时器,即告诉timer_settime()函数,哪个定时器需要启动或停止,该参数就是上面timer_create()函数的第三个参数。

关于value和old_value,其定义和setitimer()中类似,可以参看前一篇博客。一般不关心定时器之前的状态,old_value设为NULL,value中包含了首次触发定时器的时间以及该定时器是否按特定的时间周期性的执行。

4. 删除定时器timer_delete()

删除定时器就相对容易理解,提供相应的定时器句柄timerid,就可以删除对应的定时器。

#define _POSIX_C_SOURCE 199309
#include 
int timer_delete(timer_t timerid);
return 0 on success, or -1 on error

5. 简单实例

下面的例子是通过POSIX下的定时器设定实时时间的两个定时器,但是触发的周期不同。信号名使用了SIGUSR1和SIGUSR2,这两个信号都是由头文件signal.h的头文件中提供的,如果还需要更多的定时器,可以参考该头文件中的其他信号名,也可以通过宏将其中的信号名变为自己熟悉的名称。

#define _POSIX_C_SOURCE 199309
#include 
#include 
#include 
#include 
#include 
#include 

#define TIMER_SIG SIGRTMAX
using namespace std;


static void sigHandler1(int sig)
{
  cout << "alarm1 has been triggered" << endl;
}

static void sigHandler2(int sig)
{
  cout << "alarm2 has been triggered" << endl;
}


int main()
{
    //struct sigaction sa;
    struct sigevent sev1;
    timer_t t1; // 第一个定时器的标识
    struct itimerspec itv1; // 声明一个定时器时间设置
    // 设置定时器参数.表示定时器从0时刻启动,每2s重复一次
    itv1.it_value.tv_sec = 0;
    itv1.it_value.tv_nsec = 1;
    itv1.it_interval.tv_sec = 3;
    itv1.it_interval.tv_nsec = 0;


    struct sigevent sev2;
    timer_t t2; // 第二个定时器的标识
    struct itimerspec itv2; // 声明一个定时器时间设置
    // 设置定时器参数.表示定时器从0时刻启动,每1s重复一次
    itv2.it_value.tv_sec = 0;
    itv2.it_value.tv_nsec = 1;
    itv2.it_interval.tv_sec = 1;
    itv2.it_interval.tv_nsec = 0;

    signal(SIGUSR1, sigHandler1);
    signal(SIGUSR2, sigHandler2);

    sev1.sigev_notify = SIGEV_SIGNAL;// 表示通过信号来通知进程计时器到时
    sev1.sigev_signo = SIGUSR1; // 设定信号的名称

    sev2.sigev_notify = SIGEV_SIGNAL;// 表示通过信号来通知进程计时器到时
    sev2.sigev_signo = SIGUSR2; // 设定信号的名称

    timer_create(CLOCK_REALTIME, &sev1, &t1);
    timer_settime(t1, 0, &itv1, NULL);

    timer_create(CLOCK_REALTIME, &sev2, &t2);
    timer_settime(t2, 0, &itv2, NULL);


    for(;;){}

    return 0;
}

程序执行的结果如下,定时器1每3秒触发一次,定时器2每1秒触发一次。

alarm1 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm1 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm1 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm1 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm2 has been triggered
alarm1 has been triggered
alarm1 has been triggered
alarm2 has been triggered

你可能感兴趣的:(Linux,c++,linux)