Linux应用层的定时器

Linux应用层的定时器

1.定时器理解

可以将其看作一个闹钟。在Linux应用编程层上,当需要实现下列几种功能时,可考虑使用定时器。

  1. 周期性执行某一项任务
  2. 在指定时间去执行某一项任务

2.几种定时器介绍

  1. 具有定时功能的库函数API接口

        1.sleep() 
        2.usleep()
        3.nanosleep()
        4.alarm()

        sleep和usleep,Linux并没有提供系统调用,他们都是在库函数中实现的;是通过调用alarm()来设定报警时间,调用sigsuspend()将进程挂起在信号SIGALARM上。(如果不忽略或不捕捉该信号,该进程会被终止???)sleep精度是1usleep精度是1微妙;使用这种方法缺点比较明显,在Linux系统中,sleep类函数不能保证精度,尤其在系统负载比较大时,一般都会有超时现象。nanosleep()则是Linux中的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠;
alarm()也是通过定时器实现的,其精度只精确到秒级,它设置的定时器执行函数是在指定时间向当前进程发送SIGALRM信号;

      在业务项目中,对于系统提供的定时器API往往很难满足我们的需求:
函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer 设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。在一个Unix进程中,不能同时使用alarmITIMER_REAL类定时器。

 

  1. 进程间隔定时器itimer

所谓“间隔定时器(Interval Timer,简称itimer)就是指定时器采用“间隔”值(interval)来作为计时方式,当定时器启动后,间隔值interval将不断减小。当interval值减到0时,我们就说该间隔定时器到期。
间隔定时器主要被应用在用户进程上。每个Linux进程都有三个相互关联的间隔定时器:

  • 真实间隔定时器(ITIMER_REAL-以系统真实的时间计算):这种间隔定时器在启动后,不管进程是否运行,每个时钟滴答都将其间隔计数器减1。当减到0值时,内核向进程发送SIGALRM信号。
  • 虚拟间隔定时器(ITIMER_VIRT-以该进程在用户态下花费的时间来计算):也称为进程的用户态间隔定时器。当虚拟间隔定时器启动后,只有当进程在用户态下运行时,一次时钟滴答才能使间隔计数器当前值it_virt_value减1。当减到0值时,内核向进程发送SIGVTALRM信号(虚拟闹钟信号),并将it_virt_value重置为初值it_virt_incr。
  • PROF间隔定时器(ITIMER_PROF-以该进程在用户态下和内核态下所费的时间来计算):当一个进程的PROF间隔定时器启动后,则只要该进程处于运行中,而不管是在用户态或核心态下执行,每个时钟滴答都使间隔计数器it_prof_value值减1。当减到0值时,内核向进程发送SIGPROF信号,并将it_prof_value重置为初值it_prof_incr

itimer在进程中每种timer类型(ITIMER_REAL, ITIMER_PROF, ITIMER_VIRT)只能使用一个,另外一点就是他是基于signal进行超时提醒,不仅和alarm,sleep这些api冲突,而且在业务代码中signal是个很不可控的机制,尽量减少使用

头文件:

#include <sys/time.h>

      结构体说明:

struct itimerval

{

  /* Interval for periodic timer */

  struct timeval it_interval;

  /* Time until next expiration */

  struct timeval it_value;   

};

struct timeval

{

  time_t      tv_sec;         /* seconds */

  suseconds_t tv_usec;        /* microseconds 1/1000000 seconds */

};

接口说明:

int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);

 

  1. POSIX定时器(号称最强大的定时器接口来自POSIX时钟系列

POSIX定时器的是为了解决间隔定时器itimer的以下问题

  • 一个进程同一时刻只能有一个同一种类型(ITIMER_REAL, ITIMER_PROF, ITIMER_VIRT)的itimer。POSIX定时器在一个进程中可以创建任意多个timer
  • itimer定时器到期后,只能通过信号(SIGALRM,SIGVTALRM,SIGPROF)的方式通知进程,POSIX定时器到期后不仅可以通过信号进行通知,还可以使用自定义信号,还可以通过启动一个线程来进行通知
  • itimer支持us级别,POSIX定时器支持ns级别

   头文件:

         #include <time.h>

   结构体说明:      

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
}

 

    接口说明:

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

   功能 创建定时器
   描述

        函数 timer_create 会创建一个timer(每进程), 返回的timer id 在调用进程中是唯一的, 创建后的timer处于停止(disarmed)状态

参数

必选

类型

说明

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

创建的timerid 通过这个指针返回.

返回值

true

OUT

On success,return 0.On error-1 is returned,and errno is set to indicate the error.

 

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

  • SIGEV_NONE:定时器到期后什么都不做,只提供通过timer_gettimetimer_getoverrun查询超时信息
  • SIGEV_SIGNAL:定时器到期后,内核会将sigev_signo所指定的信号,传送给进程,在信号处理程序中,si_value会被设定为sigev_value的值
  • SIGEV_THREAD定时器到期后,内核会以sigev_notification_attributes为线程属性创建一个线程,线程的入口地址为sigev_notify_function,传入sigev_value作为一个参数
  1. int timer_settime(timer_t timerid, int flags,const struct itimerspec *new_value,struct itimerspec *old_value)

          功能: 启动/停止或重置定时器

参数

必选

类型

说明

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.

 

  1. 自定义定时器

     使用select()
     使用gettimeofdaydifftime

      需要注意,当系统中的一个模块需要频繁的获取系统时间,使用linux中内置的函数开销过大,使用gettimeofday做定时器的弊端也就呈现。

参考:

https://www.jianshu.com/p/f367de55ab52

https://blog.csdn.net/weixin_30808253/article/details/96362124

示例:posix

#include <stdio.h>

#include "timer.h"

#include <signal.h>

#include <limits.h>



TimerEvent_t obj,obj1,obj2;

void handle(void * context)

{

    TimerStop(&obj);

}

int main()

{

    TimerInit(&obj,handle);

    TimerSetValue(&obj,10);

    TimerStart(&obj);

   

    while(1);

  return 0;

}


#include "timer.h"

#include <signal.h>

#include <time.h>

#include <sys/time.h>



void TimerInit( TimerEvent_t *obj, void ( *callback )( void *context ) )

{

    int ret;

    memset (&(obj->sevp), 0, sizeof (obj->sevp));

    obj->sevp.sigev_value.sival_ptr = &(obj->timerid);

    obj->sevp.sigev_notify = SIGEV_THREAD;

    obj->sevp.sigev_notify_function = callback;

    ret = timer_create(CLOCK_REALTIME, &(obj->sevp), &(obj->timerid));

    printf("TimerInit----timerid:%d\n",obj->timerid);

    if( ret == 0)

      printf("timer_create\n");

   

   

   

}



void TimerStart( TimerEvent_t *obj )

{

    int ret;

    obj->new_value.it_interval.tv_sec = 0;     

    obj->new_value.it_interval.tv_nsec = 0;      

    ret = timer_settime(obj->timerid, CLOCK_REALTIME, &(obj->new_value), NULL);

   

   // printf("TimerStart-----timerid:%d\n",obj->timerid);

   // if( ret )

   //     printf("TimerStart\n");



}



void TimerStop( TimerEvent_t *obj )

{

    int ret;

    obj->new_value.it_interval.tv_sec = 0;     

    obj->new_value.it_interval.tv_nsec = 0;        

    obj->new_value.it_value.tv_sec = 0;     

    obj->new_value.it_value.tv_nsec = 0;

       

    ret = timer_settime(&(obj->timerid), CLOCK_REALTIME, &(obj->new_value), NULL);

  //  if( ret )

   //     printf("TimerStop\n");

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值