struct sigevent与定时器

介绍

最强大的定时器接口来自POSIX时钟系列,其创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()、timer_settime()、timer_delete()

创建定时器

**int timer_create(clockid_t clock_id, struct sigevent evp, timer_t timerid)

  1. clock_id说明定时器属于的时钟序号,*timerid装载的是被创建的定时器ID。函数创建一个定时器并指向timerid。

  2. 参数evp指定定时器到期产生的异步通知,evp为NULL则产生默认信号。若要产生非默认信号,则设置

    evp->sigev_signo为期望新号码,evp->sigev_notify为通知行动。通常sigev_notifySIGEV_SIGNAL,说明到期时产生一个新号。程序可以将其设置为SIGEV_NONE来防止定时器到时产生信号;

  3. 如果几个定时器产生了同一信号,处理程序可以用evp->sigev_value来区分。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志SA_SIGINFO

  4. clock_id取值CLOCK_REALTIME时为系统真实时间;

  5. sigenv结构体

    struct sigevent
    {
     int sigev_notify;   // notifiction type
        int sigev_signo; // signal number
        union sigval sigev_value;    // signal value
        void (*sigev_notify_function)(union sigval); // sigev_notify=SIGEV_THREAD
        pthread_attr_t *sigev_notifiction_attributes;
    }
    
    union sigval
    {
        int sigval_int;      //  integer value
        void *sigval_ptr;    //  pointer value
    }
    

    sigev_notify = SIGEV_NONE时只提供通过timer_gettimetimer_getoverrun查询超时信息;

    sigev_notify = SIGEV_SIGNAL当定时器超时内核将sigev_signo所指定的新号传送给进程。在信号处理程序中,si_val会被设置成sigev_value

    sigev_notify = SIGEV_THREAD当定时器到期内核会在此进程内以sigev_notifiction_attributes为线程参数创建一个线程,并且执行sigev_notify_function,传入sigev_value作为一个参数。

启动一个定时器

int timer_settime(timer_t timerid, int flags, const struct itimerspec * value, struct itimerspec ovalue)
timer_create()创建的定时器并未启动,将它关联到一个到期时间以及启动始终周期,可以使用
timer_settime()*

  1. itimespec结构体

    struct itimespec
    {
        struct timespec it_interval;
        struct timespec it_value;
    }
    

    it_value用于指定当前定时器到期时间。当定时器到期,则it_value会被更新为it_interval的值。若it_interval为0则定时器不是一个时间间隔定时器,一旦it_value到期就回到未启动的状态。

  2. timespec结构体提供了纳秒级别分辨率:

    struct timespec
    {
        timet_t tv_sec;
        long tv_nsec;
    }
    

    ovalue不是NULL,则之前的定时器到期时间会被存入ovalue。如果定时器之前处于未启动的状态,则此结构成员都为0。

获得一个活动定时器的剩余时间

*int timer_gettime(timer_t timerid, struct itimierspec value)

取得定时器的超限运行次数

int timer_getoverrun(timer_t timerid)

  1. 有可能一个定时器到期了,而这个定时器上一次产生的新号还处于挂起状态。这种情况下,其中的一个新号可能会丢失。这就是定时器超限。
  2. 通过调用timer_getoverrun()函数来确定定时器超限次数。超限只出现在同一定时器上,多个定时器产生信号会排队而不会丢失。

删除定时器

int timer_delete(timer_t timerid)

执行成功返回0,失败返回-1并将errno设为EINVAL,这个唯一的错误情况代表timerid不是一个有效定时器。

示例

int create_suicide_timeout(int sec_to_suicide)
{
        timer_t timerid;
        struct sigevent sev;
        sigset_t mask;
        struct itimerspec its;
        struct sigaction sa;

        sa.sa_sigaction = suicide_timeout_handler;
        sigemptyset(&sa.sa_mask);
        if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
                perror("sigaction");
                return -1;
        }

        sigemptyset(&mask);
        sigaddset(&mask, SIGRTMIN);
        if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1) {
                perror("sigprocmask(SIG_SETMASK)");
                return -1;
        }

        sev.sigev_notify = SIGEV_SIGNAL;
        sev.sigev_signo = SIGRTMIN;
        sev.sigev_value.sival_ptr = &timerid;
        if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
                perror("timer_create");
                return -1;
        }

        its.it_value.tv_sec = sec_to_suicide;
        its.it_value.tv_nsec = 0;
        its.it_interval.tv_sec = its.it_value.tv_sec;
        its.it_interval.tv_nsec = its.it_value.tv_nsec;
        if (timer_settime(timerid, 0, &its, NULL) == -1) {
                perror("timer_settime");
                return -1;
        }

        if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) {
                perror("sigprocmask(SIG_UNBLOCK)");
                return -1;
        }
        return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。