POSIX定时器


POSIX提供了一套定时器API,通过产生一个sigevent事件,来通知进程事件产生。


创建定时器

#include <signal.h>
#include <time.h>

int timer_create(clockid_t clockid, struct sigevent *restrict evp,
                    timer_t *restrict timerid);

函数timer_create()创建一个定时器。clockid指定使用的时钟;evp指定定时器超时时产生事件的类型(sigevent相关内容可以看这里);timerid返回计时器id,用于唯一标识一个计时器。

如果参数evp为NULL,则与传入一个字段sigev_notify为SIGEV_SIGNAL,字段sigev_signo为默认信号(在Linux下是SIGALRM),sigev_value为计时器id的sigevent结构体效果一样。

如果函数调用成功,timer_create()将返回0,并设置timerid为新创建的定时器id。如果发生错误,则返回-1,并设置errno,timreid的值未定义。


定时器操作

#include <time.h>

struct itimerspec {
    struct timespec it_interval;
    struct timespec it_value;
};

int timer_getoverrun(timer_t timerid);
int timer_gettime(timer_t timerid, struct itimerspec *value);
int timer_settime(timer_t timerid, int flags,
                    const struct itimerspec *restrict value,
                    struct itimerspec *restrict ovalue);

以上三个函数用于控制或查看timerid所指定的定时器。

函数timer_gettime()用于得到下一次超时的剩余时间和每一次超时的超时间隔,并将其存储到value指向的itimerspec结构体中。字段it_value存储直到下次超时还需要的时间如果timer处于关闭状态则设置为0值,字段it_interval存储每一次超时的超时间隔。

函数timer_settime()用于指定timer的超时时间并启动timer。value的it_value字段指定第一次超时的时间;如果it_value为0值,则timer被关闭并停止计时。value的it_interval字段指定每次超时后重新设定的超时间隔,如果it_value为0值,则timer只超时一次。

如果在参数flags中设置TIMER_ABSTIME,则value的it_value字段将被看作绝对时间。
如果参数ovalue不为NULL则,下一次超时的时间和每一次超时的超时间隔。

由于同一时间里只会由一个信号处于未决状态,如果在信号处理程序响应之前有多个超时信号产生,那么多余的信号会丢失,只留下一个实例。timer_getoverrun()用于得到定时器的溢出计数。即在信号生成与接收之间发生的定时器到期额外次数。如果上次收到信号后定时器发生了3次到期,那么溢出计数是2。

timer_getoverrun()在调用成功时返回定时器的溢出计数。timer_gettime()和timer_settime()在调用成功时,返回0。
以上三个函数在调用失败时返回-1,并设置errno


删除计时器

#include <signal.h>
#include <time.h>

int timer_delete(timer_t timerid);

函数timer_delete()用于删除timerid指定的计时器。
调用成功时timer_delet()返回零值。否则返回-1并设置errno。

例子

#include <time.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>

bool looping = true;
int count = 0;

void sig_handle(int signo)
{
    printf("Cought signo: %d count: %d\n", signo, ++count);
    if (count == 5)
        looping = false;
}

int main()
{
    timer_t timer;
    struct itimerspec newit;
    struct timespec sl = {
        .tv_sec = 0,
        .tv_nsec = 200000000
    };
    struct sigaction sa;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = sig_handle;
    sigaction(SIGALRM, &sa, NULL);

    timer_create(CLOCK_REALTIME, NULL, &timer);

    newit.it_value.tv_sec = 1;
    newit.it_value.tv_nsec = 0;
    newit.it_interval.tv_sec = 1;
    newit.it_interval.tv_nsec = 0;

    timer_settime(timer, 0, &newit, NULL);

    while (looping) {
        printf("do something...\n");
        nanosleep(&sl, NULL);
    }

    return 0;
}
do something...
do something...
do something...
do something...
do something...
Cought signo: 14 count: 1
do something...
do something...
do something...
do something...
do something...
Cought signo: 14 count: 2
do something...
do something...
do something...
do something...
do something...
Cought signo: 14 count: 3
do something...
do something...
do something...
do something...
do something...
Cought signo: 14 count: 4
do something...
do something...
do something...
do something...
do something...
Cought signo: 14 count: 5

注意

  1. 计时器不应该被fork()产生的子进程继承,并且应给被exec*停止计时并删除。
  2. 在创建计时器时,如果事件类型设置为SIGEV_THREAD,且sigev_notify_attributes指向一个设置过pthread_attr_setstack()的属性,那么如果信号产生多次,将导致未定义结果。
  3. 在还有sigevent时间处于未决状态时停止或重设定时器的效果是未定义的。
posted @ 2020-11-29 21:34  Sigmun  阅读(967)  评论(0)    收藏  举报