Linux定时器————POSIX Timer

文章内容主要来自  

linux下定时器介绍1

Linux中Posix定时器的使用

POSIX Timer

  间隔定时器 setitimer 有一些重要的缺点,POSIX Timer 对 setitimer 进行了增强,克服了 setitimer 的诸多问题:

  • 一个进程同一时刻只能有一个 timer。假如应用需要同时维护多个 Interval 不同的计时器,必须自己写代码来维护。这非常不方便。使用 POSIX Timer,一个进程可以创建任意多个 Timer。
  • setitmer 计时器时间到达时,只能使用信号方式通知使用 timer 的进程,而 POSIX timer 可以有多种通知方式,比如信号,或者启动线程。
  • 使用 setitimer 时,通知信号的类别不能改变:SIGALARM,SIGPROF 等,而这些都是传统信号,而不是实时信号,因此有 timer overrun 的问题;而 POSIX Timer 则可以使用实时信号。
  • setimer 的精度是 ms,POSIX Timer 是针对有实时要求的应用所设计的,接口支持 ns 级别的时钟精度。

一、POSIX Timer函数接口

二、POSIX Timer参数接口

2.1 sigevent结构体

 struct sigevent {
     sigval_t sigev_value;
     int sigev_signo;     /* Notification signal */
     int sigev_notify;     /* Notification method */
     union {
         int _pad[SIGEV_PAD_SIZE];
         int _tid;

         struct {
             void (*function)(sigval_t);
             void* _attribute;
         } _sigev_thread;
     } _sigev_un;
} sigevent_t;

 2.1.1 sigval_t

typedef union sigval{
        int sigval_int;
        void *sival_ptr;    
}sigval_t;

 用于指定信号的参数。

2.1.2 sigev_signo

  • SIGEV_NONE:定时器到期时不产生通知.
  • SIGEV_SIGNAL:定时器到期时将给进程投递一个信号,sigev_signo 可以用来指定使用什么信号。
  • SIGEV_THREAD:定时器到期时将启动新的线程进行需要的处理。
  • SIGEV_THREAD_ID(仅针对 Linux):定时器到期时将向指定线程发送信号。

     SIGEV_SIGNAL 方式:使用者可以选择使用什么信号,用 sigev_signo 表示信号值,比如 SIG_ALARM。

    SIGEV_THREAD 方式:需要设置 function,当 Timer 到期时,将使用该函数作为入口启动一个线程来处理信号;sigval_t 保存了传入 sigev_notify_function 的参数。_attribute 如果非空,则应该是一个指向 pthread_attr_t 的指针,用来设置线程的属性(比如 stack 大小,detach 状态等)。

    SIGEV_THREAD_ID方式: linux独有,发出一个信号,和signal类似,只不过该信号发送到指定的线程,如果 sigev_notify 设置该值时,需要同时指定 _sigev_un._tid 的值,例如使用 gettid();

2.1.3 _pad[SIGEV_PAD_SIZE]

    这个似乎是用来对齐的

#define __ARCH_SIGEV_PREAMBLE_SIZE (sizeof(int)*2 + sizeof(sigval_t))
#define SIGEV_MAX_SIZE 64
#define __SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) / sizeof(int))

在64为计算机上sigevent_t整个占64字节。

2.2 clockid_t类型

clockid定义了定时器计时的方法,有如下几个值:

  • CLOCK_REALTIME : 时间是系统保存的时间,即可以由 date 命令显示的时间,该时间可以重新设置。比如当前时间为上午 10 点 10 分,Timer 打算在 10 分钟后到时。假如 5 分钟后,修改当前时间为 10 点 10 分,那么 Timer 还会再等十分钟到期,因此实际上 Timer 等待了 15 分钟。
  • CLOCK_MONOTONIC : 单调递增的时钟,系统启动后不会被改变(重启后重新开始计时)。
  • CLOCK_PROCESS_CPUTIME_ID : 用于测量当前进程(包括所有线程)CPU占用时间,包含用户调用和系统调用;
  • CLOCK_THREAD_CPUTIME_ID : 用于测量当前线程CPU占用时间,包含用户调用和系统调用;

2.3 itimerspec结构体

struct timespec {
    time_t tv_sec;    /* 表示秒 */
    long   tv_nsec;    /* 表示纳秒 */
};
 
struct itimerspec {
    struct timespec it_interval;  /* 定时器的时间周期 */
    struct timespec it_value;     /* 定时器的时间值 */
};

     time_t类型是一个long long int类型。可以采用C标准库做时间转换,见time.h头文件详解。

  • 如果it_value 中两个值都为0,表示关闭定时器;如果it_value 中至少一个不为0,则表示打开定时器;

  • 如果it_interval中两个值都为0,表示定时器只执行1次;如果 it_interval 中至少一个不为0,则表示定时器是周期性工作;

三、函数详解

3.1 timer_create

创建一个定时器,函数原型:

int timer_create(clockid_t clockid, struct sigevent* event, timer_t* timer_ptr);

timer_ptr 定时器的标识 ID。如果event被设置为NULL,相当于SIGEV_SIGNAL,信号是SIGALRM;

3.2 timer_delete

删除定时器,函数原型:

int timer_delete(timer_t timer);

3.3 timer_gettime

用于查询某个定时器参数,函数原型:

int timer_gettime(timer_t timer, struct itimerspec* ts);

3.4 timer_settime

用以启动或停止一个定时器,函数原型:

int timer_settime(timer_t timer, int flags, const struct itimerspec* new_value, struct itimerspec* old_value);

flags,设置定时器时间标识:

  • 0,启动一个相对定时器,基于当前时间 + 指定的 new_value;
  • TFD_TIMER_ABSTIME,使用绝对时间的定时器,由参数 new_value 决定;
  • TFD_TIMER_CANCEL_ON_SET,如果实时时钟发生改变,退出绝对时间定时器;

old_value,定时器旧的定时值,最开始设置为NULL,如果为非NULL,表示之前设置过;

3.5 timer_getoverrun()

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的,不是信号,就是timer发出的信号处于未决时,下一个信号会被丢弃,是为了防止信号排满队列,比如定时器发出的频率太快,处理不来,不能让队列中都是这个定时器,或者因调度问题,接收信号进程未及时处理信号等等。

参考

linux下定时器介绍1

Linux中Posix定Linux中Posix定时器的使用时器的使用

POSIX Timer

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值