Linux中Posix定时器的使用

在上一节已经了解到Linux上使用各种定时器的优缺点,接下来主要介绍Posix定时器。
传送门:关于Linux应用层的定时器

posix定时器还是比较简单的,主要就在于学习API的使用。

API接口详细说明

  • int timer_create(clockid_t clockid, struct sigevent *sevp,timer_t timerid)

功能: 创建定时器
描述: 函数 timer_create 会创建一个timer(每进程), 返回的timer id 在调用进程中是唯一的, 创建后的timer处于停止(disarmed)状态.
timer_create 官方文档说明

参数 必选 类型 说明
clock_id ture IN 说明定时器是基于哪个时钟的,可有以下取值:
1. CLOCK_REALTIME :Systemwide realtime clock.(系统实时时间,即日历时间)
2. CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.(从系统启动开始到现在为止的时间)
3.CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer(本进程启动到执行到当前代码,系统CPU花费的时间).
4. CLOCK_THREAD_CPUTIME_ID :Thread-specific timer(本线程启动到执行到当前代码,系统CPU花费的时间).
5.CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME(CLOCK_REALTIME的细粒度(高精度)版本).
6.CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC(CLOCK_MONOTONIC的细粒度版本).
*sevp true IN 设置了定时器到期时的通知方式和处理方式(结构体详细定义参见下方)
该类型结构体使用前要初始化(memset等), 否则可能出现timer到期无动作的异常情况.
*timerid true OUT 创建的timer的id 通过这个指针返回.
返回值 true OUT On success,return 0.On error,-1 is returned,and errno is set to indicate the error.


  • int timer_delete (timer_t timerid)

功能: 通过timder id删除指定的 timer
描述: 如果调用这个函数时,timer处于激活(armed)状态, 删除前会先将这个timer状态变更为未激活(disarmed)状态.
挂起等待timer(删除的)产生信号的行为是未定义的.
timer_delete 官方文档说明

参数 必选 类型 说明
timerid true IN 指定的timer
返回值 true OUT On success,return 0.On error,-1 is returned,and errno is set to indicate the error.


  • int timer_settime(timer_t timerid, int flags,const struct itimerspec *new_value,struct itimerspec *old_value)

功能: 启动/停止或重置定时器
描述:
timer_settime 官方文档说明

参数 必选 类型 说明
timerid true IN 指定的timer
flags true IN 0 :new_value->it_value 表示希望timer首次到期时的时间与启动timer的时间间隔.
TIMER_ABSTIME:new_value->it_value 表示希望timer首次到期的绝对时间.
(如果new_value->it_value 设定的绝对时间 早于 当前的绝对时间, 那么timer会立即到期.)
(如果时钟 CLOCK_REALTIME 被调整了,那么timer的首次过期时间也会适当调整.)
new_value true IN new_value 有2个子域: it_value 和 it_interval
⑴ it_value : 用于设置首次timer到期的时间, 也是 启动/停止 timer的控制域;
⑵ it_interval : 用于设置timer循环的时间间隔, 如果其值不为0(秒和纳秒至少有一个不为0),每次timer到期时,timer会使用new_value->it_interval的值重新加载timer;
如果其值为0, timer只会在it_value指定的时间到期一次,之后不会重新加载timer.
①启动timer:
前提:timer处于停止(disarmed)状态,否则就是重置timer.
设置:new_value->it_value的值是非0值(秒和纳秒都不等于0或者其中一个不等于0).
结果:timer变为启动(armed)状态.
② 停止timer:
设置:new_value->it_value的的值为0(秒和纳秒都为0).
结果:timer变为停止(disarmed)状态.
③重置timer:
前提:timer处于已启动(armed)状态,否则就是启动timer
设置:new_value->it_value的的值不为0(秒和纳秒至少有一个不为0)
结果:timer仍为(armed)状态.之前的参数(即new_value(it_value 和 it_interval))设置会被覆盖.
old_value true OUT 取得上一次的设置的new_value
返回值 true OUT On success,return 0.On error,-1 is returned,and errno is set to indicate the error.


  • int timer_gettime(timer_t timerid, struct itimerspec *curr_value)

功能: 获得定时器的到期时间和间隔
描述:
timer_gettime 官方文档说明

参数 必选 类型 说明
timerid true IN 指定的timer
curr_value true OUT curr_value->it_value : 离timer到期的剩余时间
curr_value->it_interval : timer的循环时间间隔
返回值 true OUT On success,return 0.On error,-1 is returned,and errno is set to indicate the error.


  • int timer_getoverrun(timer_t timerid)

功能: 获得定时器超限的次数
描述:当一个timer到期并且上一次到期时产生的信号还处于挂起状态时,不会产生新的信号(即丢弃一个信号),这就是定时器超限(overrun), 丢弃的信号数量就是 overrun count。
对于一个给定的timer, 在任何时间点只能有一个信号在进程中排队, 这是POSIX.1-2001中指定的, 因为不这样做,排队信号的数量很容易达到系统的上限.
因为系统调度延迟或者信号被暂时阻塞都会造成信号产生到信号被发送( (e.g., caught by a signal handler))或者接收((e.g., using sigwaitinfo(2)))之间有一个延迟的时间段,在这个时间段中可能会有多次的timer到期.
程序可以通过调用timer_getoverrun来确定一个指定的定时器出现这种超限的次数, 从而精确能精确的计算出在给定时间内timer到期了多少次。
定时器超限只能发生在同一个定时器产生的信号上。多个定时器,甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。
如果超限运行的次数等于或大于 {DELAYTIMER_MAX},则此调用会返回 {DELAYTIMER_MAX}.
timer_getoverrun 官方文档说明

参数 必选 类型 说明
timerid true IN 指定的timer
返回值 true OUT On success,return 0.On error,-1 is returned,and errno is set to indicate the error.


结构体定义说明:

. struct sigevent结构的定义如下:

struct sigevent
{
    int sigev_notify;   //设置定时器到期后的行为
    int sigev_signo;    //设置产生信号的信号码
    union sigval   sigev_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
}

如果sevp传入NULL,那么定时器到期会产生默认的信号,对CLOCK_REALTIMER来说,默认信号就是SIGALRM,如果要产生除默认信号之外的其他信号,程序必须将evp->sigev_signo设置为期望的信号码。
sigev_notify:的值可取以下几种:

  • SIGEV_NONE:定时器到期后什么都不做,只提供通过timer_gettime和timer_getoverrun查询超时信息。
  • SIGEV_SIGNAL:定时器到期后,内核会将sigev_signo所指定的信号,传送给进程,在信号处理程序中,si_value会被设定为sigev_value的值。
  • SIGEV_THREAD:定时器到期后,内核会以sigev_notification_attributes为线程属性创建一个线程,线程的入口地址为sigev_notify_function,传入sigev_value作为一个参数。


. struct itimerspec结构的定义如下:

struct itimerspec
{
    struct timespec it_interval;    // 时间间隔
    struct timespec it_value;       // 首次到期时间
};
 
struct timespec
{
    time_t  tv_sec    //Seconds.
    long    tv_nsec   //Nanoseconds.
};

定时器工作时,先将it_value的时间值减到0,发送一个信号,再将it_interval的值赋给it_value,重新开始定时,如此反复。如果it_value值被设置为0,则定时器停止定时;如果it_interval等于0,那么表示该定时器不是一个时间间隔定时器,一旦it_value到期后定时器就回到未启动状态。


lai个例子:

/*************************************************************************
    > File Name       : posix_timer.c
    > Author          : Jalyn
    > Mail            : JalynFang@outlook.com 
    > Created Time    : 2018年11月11日 星期日 18时27分09秒
 ************************************************************************/
#include<stdio.h>
#include <signal.h>
#include <time.h>

void  function_timer()
{
    time_t t;
    char p[32];
    time(&t);
    //strftime(p, sizeof(p), "%T", localtime(&t));
    strftime(p, sizeof(p), "%Y-%m-%d %H:%M:%S", localtime(&t));

    printf("@Jalyn debug -----> date: %s \n", p);
}


int main(int argc,char *argv[])
{
    int ret;
    timer_t timer;
    struct sigevent evp;
    struct timespec spec;
    struct itimerspec time_value;

    evp.sigev_value.sival_ptr = &timer;
    /*定时器到期时,会产生一个信号*/
    evp.sigev_notify = SIGEV_SIGNAL; 
    evp.sigev_signo = SIGUSR1;
    signal(SIGUSR1, function_timer);

    /*时钟源选CLOCK_MONOTONIC主要是考虑到系统的实时时钟可能会在
    程序运行过程中更改,所以存在一定的不确定性,而CLOCK_MONOTONIC
    则不会,较为稳定*/
    ret = timer_create(CLOCK_MONOTONIC, &evp, &timer);
    if( ret )
        perror("timer_create");

    time_value.it_interval.tv_sec = 1;      /*每秒触发一次*/
    time_value.it_interval.tv_nsec = 0;
    clock_gettime(CLOCK_MONOTONIC, &spec);         
    time_value.it_value.tv_sec = spec.tv_sec + 5;      /*5秒后启动*/
    time_value.it_value.tv_nsec = spec.tv_nsec + 0;

    ret = timer_settime(timer, CLOCK_MONOTONIC, &time_value, NULL);
    if( ret )
            perror("timer_settime");

    while(1)
    {
        printf("@Jalyn debug -----> main loop \n");
        sleep(1);
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容