Posix定时器使用总结

参考:

timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它) - 陈木 - 博客园 (cnblogs.com)

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

创建一个定时器

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

进程可以通过调用timer_create()创建特定的定时器,定时器是每个进程自己的,不是在fork时继承的。该函数创建了定时器,并将他的ID 放入timerid指向的位置中。clock_id说明定时器是基于哪个时钟的,*timerid装载的是被创建的定时器的ID。evp指定了定时器到期要产生的异步通知。如果evp为NULL,那么定时器到期会产生默认的信号,对 CLOCK_REALTIMER来说,默认信号就是SIGALRM。如果要产生除默认信号之外的其它信号,程序必须将 evp->sigev_signo设置为期望的信号码。struct sigevent 结构中的 成员evp->sigev_notify说明了定时器到期时应该采取的行动。通常,这个成员的值为SIGEV_SIGNAL,这个值说明在定时器到期时,会产生一个信号。程序可以将成员evp->sigev_notify设为SIGEV_NONE来防止定时器到期时产生信号。如果几个定时器产生了同一个信号,处理程序可以用 evp->sigev_value来区分是哪个定时器产生了信号。要实现这种功能,程序必须在为信号安装处理程序时,使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO。(这一点要参考信号相关内容)

第一个参数

clock_id取值为以下:

CLOCK_REALTIME :Systemwide realtime clock.

CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.

CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.

CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.

CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.

CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.

通常,选择CLOCK_REALTIME即可。

第二个参数

主要是用来设置定时器的模式以及回调方式。

我们来看看第二个参数evp的类型定义

struct sigevent
{
    int sigev_notify; //notification type
    int sigev_signo; //signal number
    union sigval   sigev_value; //signal value
    void (*sigev_notify_function)(union sigval);
    pthread_attr_t *sigev_notify_attributes;
}

其中,结构体第三个元素是个联合体,定义如下
union sigval
{
    int sival_int; //integer value
    void *sival_ptr; //pointer value
}

 通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为:

SIGEV_NONE:什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。

SIGEV_SIGNAL:当定时器到期,内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中,si_value会被设定会sigev_value。

由此可知,sigev_signo是指定要发送的信号的;同时,sigev_value的值会随信号传递到信号处理函数中。所以,这个模式下不需要定时器回调函数,而是需要另外按照信号的处理方式来处理定时器发送的信号,注意,这个信号是定时器到期后由内核发送的。

SIGEV_THREAD:当定时器到期,内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为为一个参数。看函数指针的类型中的参数类型就能知道了。

由此可知,这种模式时,需要注册定时回调函数,也会将sigev_value传入。

sigev_value要传递什么数据应该是自己来定义的,传指针还是传值,都根据需求来,这个是个联合体,后给哪个赋值,就是哪个。 

第三个参数

timerid装载的是被创建的定时器的ID,没啥好说的。 

启动一个定时器

timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()。

#include <time.h>
int timer_settime(timer_t timerid, int flags, 
                    const struct itimerspec *value, struct itimerspec *ovalue);

timerid,就是创建时指定的定时器id,好理解;

Flags,相对时间还是绝对时间,0(默认,非TIMER_ABSTIME的含义)或者TIMER_ABSTIME,如果flags的值为TIMER_ABSTIME,则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。

Value,目标的定时时间信息,具体看下文的struct itimespec信息

Ovalue,可设置为NULL,ovalue的值不是NULL,则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态,则此结构的成员全都会被设定成0。

value和ovalue的指针类型都是struct itimerspec

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

it_interval其实就是个定时器的重装载值,it_value用于指定当前的定时器到期时间。当定时器到期,it_value的值会被更新成it_interval 的值。如果it_interval的值为0,则定时器不是一个时间间隔定时器,一旦it_value到期就会回到未启动状态,也就是说是一次性的。

其中,这两个参数的类型仍然是个结构体,timespec的结构提供了纳秒级分辨率:

struct timespec{
    time_t tv_sec;
    long tv_nsec; 
};

实际设置时,需要设置到tv_sec和tv_nsec这一步,用哪个单位,就设置哪个单位的值,另一个的值设为0即可。

posix的settime和system v的setitimer函数很类似:

linux c setitimer使用方法说明 - zfyouxi - 博客园 (cnblogs.com)

可作为补充参考。

删除一个定时器

int timer_delete (timer_t timerid);

一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。执行失败时,此调用会返回-1并将errno设定会 EINVAL,这个唯一的错误情况代表timerid不是一个有效的定时器。

实际使用

定时回调,和定时发信号然后捕获信号进行处理,二者的大体思路都是类似的。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>

timer_t customtimer;

void custom_timer_handler(union sigval sv)
{
    printf("value is %d\n", sv.sival_int);
}

int main(int argc, char *argv[])
{
    struct sigevent evp;
    struct itimerspec itspec;

    memset(&evp, 0, sizeof(evp));//不加这一句就报错了
    evp.sigev_notify = SIGEV_THREAD;
    evp.sigev_notify_function = custom_timer_handler;
    evp.sigev_value.sival_int = 200;
    timer_create(CLOCK_REALTIME, &evp, &customtimer);

    itspec.it_interval.tv_sec = 1;
    itspec.it_interval.tv_nsec = 0;
    itspec.it_value.tv_sec = 1;
    itspec.it_value.tv_nsec = 0;
    timer_settime(customtimer, 0, &itspec, NULL);

    while(1)
    {
        sleep(20);
    }

    return 0;
}

发现个问题,加了memset(&evp, 0, sizeof(evp));就可以,不加这一句就执行报错,段错误

另外,定时器别忘了加这两个头文件

#include <time.h>

#include <signal.h>

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POSIX定时器库中,有三个主要的函数用于操作定时器,它们分别是timer_create()、timer_settime()和timer_delete()。 这些函数可以帮助我们创建定时器、设置定时器的时间和操作定时器的删除。 此外,POSIX定时器库还提供了一套API来处理定时器事件。通过产生一个sigevent事件,来通知进程定时器事件的产生。这样,我们可以在定时器到期时执行一些特定的操作。 在POSIX定时器库中,我们还可以使用CLOCK_REALTIME时钟,它是一个系统范围的实时时钟。基于这个时钟的定时器可以在处理器处于省电模式时唤醒处理器并触发相应的事件。 通过使用这些函数和时钟,我们可以实现各种定时器功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [POSIX 定时器](https://blog.csdn.net/Little_Eyelash/article/details/117918411)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [POSIX定时器](https://blog.csdn.net/m0_52152959/article/details/110354542)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值