当前位置: 首页 > 工具软件 > Timer > 使用案例 >

timer_create系列定时器函数

符正信
2023-12-01

定时器函数之timer_create系列

在调试mbim信令得sar level问题中,因为我们sar level这个信令和modem得注册是我们手动注册得,如果我们modem侧重启sar level得信令就会失去作用从而无法设置sar level状态。

在我们调试代码中,定时器起着非常大得作用,在测试中我们可以使用sleep, alarm信号,muduo网络库还有timerfd_create函数,还可以使用sigpromask和sigsuspend组合来实现定时,但是这里我们讲timer_creater实现得定时。

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

创建一个定时器:

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

功能:创建一个POSIX标准得进程定时器

参数:@clock_id 可选系统得宏,

​ @sevp环境值,结构体struct sigevent变量得地址

​ @timerid定时器标识, 结构体timer_t变量得地址

编译:link with -lrt

返回值: 0–成功 ; -1 --失败 errno被设置

进程可以通过调用timer_create()创建特定得定时器,定时器是每个进程自己得,不是在fork时继承得。clock_id说明定时器是基于哪个时钟得,timerid装载得是被创建的定时器的ID。该函数创建了定时器,并将他的ID放入了timerid只指向的位置中。参数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.

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_gettimer和timer_getoverrun查询超时信息。

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

SIGEV_THRED :当定时器到期,内核会(在此进程内)以sigev_notification_attrbutes为线程属性创建一个线程,并且让它执行sigev_notify_function,传入sigev_value作为一个参数。

启动一个定时器:

timer_creater只是创建一个定时器,并未启动。要将它关联到一个到期时间以及启动始终周期,可以使用timer_settime()

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);

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

it_value用于指定当前的定时器到期时间。当定时器到期,it_value的值被更新成it_interval的值。如果it_interval的值为0,则定时器不是一个时间间隔定时器,一旦it_value到期就会回到未启动状态。timerspec的结构提供了纳秒级分辨率:

struct timespec{
   time_t tv_sec;
   long tv_nsec;  
};

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

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

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

int timer_gettime(timer_t timerid,struct itimerspec \*value);

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

有可能一个定时器到期了,而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下,其中的一个信号可能会丢失。这就是定时器超限。程序可以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器,甚至是那些使用相同的时钟和信号的定时器,所产生的信号都会排队而不会丢失。

int timer_getoverrun(timer_t timerid);

执行成功时,timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来说,在我们之前的例子中,一个1ms的定时器运行了10ms,则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX,则此调用会返回DELAYTIMER_MAX。

执行失败时,此函数会返回-1并将errno设定会EINVAL,这个唯一的错误情况代表timerid指定了无效的定时器。

删除一个定时器:

int timer_delete (timer_t timerid);

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

举例1:采用新线程进驻的通知方式


#include <stdio.h> 
#include <signal.h> 
#include <time.h> 
#include <string.h> 
#include <stdlib.h> 
#include <unistd.h> 
 
void timer_thread(union sigval v) 
{ 
    printf("timer_thread function! %d\n", v.sival_int); 
} 
 
int main() 
{ 
    timer_t timerid; 
    struct sigevent evp; 
    memset(&evp, 0, sizeof(struct sigevent));       //清零初始化 
 
    evp.sigev_value.sival_int = 111;                //也是标识定时器的,回调函数可以获得 
    evp.sigev_notify = SIGEV_THREAD;                //线程通知的方式,派驻新线程 
    evp.sigev_notify_function = timer_thread;       //线程函数地址 
 
    if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) 
    {   
        perror("fail to timer_create"); 
        exit(-1); 
    }   
 
    /* 第一次间隔it.it_value这么长,以后每次都是it.it_interval这么长,就是说it.it_value变0的时候会>装载it.it_interval的值 */
    struct itimerspec it; 
    it.it_interval.tv_sec = 1;  // 回调函数执行频率为1s运行1次
    it.it_interval.tv_nsec = 0; 
    it.it_value.tv_sec = 3;     // 倒计时3秒开始调用回调函数
    it.it_value.tv_nsec = 0; 
 
    if (timer_settime(timerid, 0, &it, NULL) == -1) 
    {   
        perror("fail to timer_settime"); 
        exit(-1); 
    }   
    while (1);
 
    return 0; 
} 
/*
 * int timer_gettime(timer_t timerid, struct itimerspec *curr_value);
 * 获取timerid指定的定时器的值,填入curr_value
 */

举例2:通知方式为信号的处理方式

#include <stdio.h> 
#include <time.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <string.h> 
#include <unistd.h> 
 
#define CLOCKID CLOCK_REALTIME 
 
void sig_handler(int signo) 
{ 
    printf("timer_signal function! %d\n", signo); 
} 
 
int main() 
{ 
    // XXX int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 
    // signum--指定的信号编号,可以指定SIGKILL和SIGSTOP以外的所有信号编号 
    // act结构体--设置信号编号为signum的处理方式 
    // oldact结构体--保存上次的处理方式 
    // 
    // struct sigaction   
    // { 
    // void (*sa_handler)(int);         //信号响应函数地址 
    // void (*sa_sigaction)(int, siginfo_t *, void *);   //但sa_flags为SA——SIGINFO时才使用 
    // sigset_t sa_mask;         //说明一个信号集在调用捕捉函数之前,会加入进程的屏蔽中,当捕捉函数返回时,还原 
    // int sa_flags; 
    // void (*sa_restorer)(void);   //未用 
    // }; 
    // 
    timer_t timerid; 
    struct sigevent evp; 
 
    struct sigaction act; 
    memset(&act, 0, sizeof(act)); 
    act.sa_handler = sig_handler; 
    act.sa_flags = 0; 
 
    // XXX int sigaddset(sigset_t *set, int signum);  //将signum指定的信号加入set信号集
    // XXX int sigemptyset(sigset_t *set);          //初始化信号集 
 
    sigemptyset(&act.sa_mask); 
 
    if (sigaction(SIGUSR1, &act, NULL) == -1) 
    { 
        perror("fail to sigaction"); 
        exit(-1); 
    } 
 
    memset(&evp, 0, sizeof(struct sigevent)); 
    evp.sigev_signo = SIGUSR1; 
    evp.sigev_notify = SIGEV_SIGNAL; 
    if (timer_create(CLOCK_REALTIME, &evp, &timerid) == -1) 
    { 
        perror("fail to timer_create"); 
        exit(-1); 
    } 
 
    struct itimerspec it; 
    it.it_interval.tv_sec = 2; 
    it.it_interval.tv_nsec = 0; 
    it.it_value.tv_sec = 1; 
    it.it_value.tv_nsec = 0; 
    if (timer_settime(timerid, 0, &it, 0) == -1) 
    { 
        perror("fail to timer_settime"); 
        exit(-1); 
    } 
 c
    pause(); 
 
    return 0; 
} 

参考:http://blog.sina.com.cn/s/blog_590be5290100j08q.html

参考:https://blog.csdn.net/sinat_36184075/article/details/80489402

 类似资料: