linux定时器总结

时间:2021-02-17 23:36:04

1   参考资料

Ø  http://www.ibm.com/developerworks/cn/linux/1307_liuming_linuxtime1/

Ø  《linux系统编程》第“10.9 定时器”章节

2   概要

要在linux中使用定时器,有如下三种方法:

定时器方式

一个进程允许

使用的数量

通知方式

简单的闹钟 - alarm

1个

Ø  信号:

SIGALRM

间歇定时器 - setitimer

1个

Ø  信号:

SIGALRM、SIGVTALRM、SIGPROF

高级定时器 - timer_create

无限制

Ø  信号:

可以自己选择用什么信号

Ø  启动线程

就不会产生信号,可以避免慢系统调用被信号中断的问题

 

有关慢系统调用被信号中断的问题可以参考我此前写的一篇文章《信号中断慢系统调用》:http://blog.csdn.net/benkaoya/article/details/17262053

3   alarm

alarm()是最简单的定时器接口,对该函数的调用会在真实时间(real time)seconds秒之后将SIGALRM信号发给调用进程。它不能自动重启(即定时器到后要再次定时,需要再调用一次alarm)。

4   setitimer

间歇定时器setitimer系统调用,他可以提供比alarm()更多的控制。它能够自动重启。

linux 为每一个进程提供了 3 个 setitimer 间隔计时器:

Ø  ITIMER_REAL:减少实际时间,到期的时候发出 SIGALRM 信号。

Ø  ITIMER_VIRTUAL:减少有效时间 (进程执行的时间),产生 SIGVTALRM 信号。

Ø  ITIMER_PROF:减少进程的有效时间和系统时间 (为进程调度用的时间)。这个经常和上面一个使用用来计算系统内核时间和用户时间。产生 SIGPROF 信号。

所谓 REAL 时间,即我们人类自然感受的时间,英文计算机文档中也经常使用 wall-clock 这个术语。说白了就是我们通常所说的时间,比如现在是下午 5 点 10 分,那么一分钟的 REAL 时间之后就是下午 5 点 11 分。

VIRTUAL 时间是进程执行的时间,Linux 是一个多用户多任务系统,在过去的 1 分钟内,指定进程实际在 CPU 上的执行时间往往并没有 1 分钟,因为其他进程会被 Linux 调度执行,在那些时间内,虽然自然时间在流逝,但指定进程并没有真正的运行。VIRTUAL 时间就是指定进程真正的有效执行时间。比如 5 点 10 分开始的 1 分钟内,进程 P1 被 Linux 调度并占用 CPU 的执行时间为 30 秒,那么 VIRTUAL 时间对于进程 P1 来讲就是 30 秒。此时自然时间已经到了 5 点 11 分,但从进程 P1 的眼中看来,时间只过了 30 秒。

PROF 时间比较独特,对进程 P1 来说从 5 点 10 分开始的 1 分钟内,虽然自己的执行时间为 30 秒,但实际上还有 10 秒钟内核是在执行 P1 发起的系统调用,那么这 10 秒钟也被加入到 PROF 时间。这种时间定义主要用于全面衡量进程的性能,因为在统计程序性能的时候,10 秒的系统调用时间也应该算到 P1 的头上。这也许就是 PROF 这个名字的来历吧。

5   POSIX Timer

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

Ø  首先,一个进程同一时刻只能有一个 timer。假如应用需要同时维护多个 Interval 不同的计时器,必须自己写代码来维护。这非常不方便。使用 POSIX Timer,一个进程可以创建任意多个 Timer。

Ø  setitmer 计时器时间到达时,只能使用信号方式通知使用 timer 的进程,而 POSIX timer 可以有多种通知方式,比如信号,或者启动线程。

Ø  使用 setitimer 时,通知信号的类别不能改变:SIGALARM,SIGPROF 等,而这些都是传统信号,而不是实时信号,因此有 timer overrun 的问题;而 POSIX Timer 则可以使用实时信号。

备注:通过kill –l可以查看系统支持的所有信号列表。编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

Ø  setimer 的精度是 ms,POSIX Timer 是针对有实时要求的应用所设计的,接口支持 ns 级别的时钟精度。

表 2. POSIX Timer 函数

函数名

功能描述

timer_create 

创建一个新的 Timer;并且指定定时器到时通知机制

timer_delete

删除一个 Timer

timer_gettime

Get the time remaining on a POSIX.1b interval timer

timer_settime

开始或者停止某个定时器。

timer_getoverrun

获取丢失的定时通知个数。

使用 Posix Timer 的基本流程很简单,首先创建一个 Timer。创建的时候可以指定该 Timer 的一些特性,比如 clock ID。clock ID 即 Timer 的种类,可以为下表中的任意一种:

表 3. POSIX Timer clock ID

Clock ID

描述

CLOCK_REALTIME

Settable system-wide real-time clock

CLOCK_MONOTONIC

Nonsettable monotonic clock

CLOCK_PROCESS_CPUTIME_ID

Per-process CPU-time clock

CLOCK_THREAD_CPUTIME_ID

Per-thread CPU-time clock

Ø  CLOCK_REALTIME 时间是系统保存的时间,即可以由 date 命令显示的时间,该时间可以重新设置。比如当前时间为上午 10 点 10 分,Timer 打算在 10 分钟后到时。假如 5 分钟后,我用 date 命令修改当前时间为 10 点 10 分,那么 Timer 还会再等十分钟到期,因此实际上 Timer 等待了 15 分钟。假如您希望无论任何人如何修改系统时间,Timer 都严格按照 10 分钟的周期进行触发,那么就可以使用CLOCK_MONOTONIC。

Ø  CLOCK_PROCESS_CPUTIME_ID 的含义与 setitimer 的 ITIMER_VIRTUAL 类似。计时器只记录当前进程所实际花费的时间;比如还是上面的例子,假设系统非常繁忙,当前进程只能获得 50%的 CPU 时间,为了让进程真正地运行 10 分钟,应该到 10 点 30 分才允许 Timer 到期。

Ø  CLOCK_THREAD_CPUTIME_ID 以线程为计时实体,当前进程中的某个线程真正地运行了一定时间才触发 Timer。

 

设置到期通知方式

timer_create 的第二个参数 struct sigevent 用来设置定时器到时时的通知方式。该数据结构如下:

清单 10,结构sigevent

 struct sigevent {
 int sigev_notify; /* Notification method */
 int sigev_signo; /* Notification signal */
 union sigval sigev_value; /* Data passed with
 notification */
 void (*sigev_notify_function) (union sigval);
 /* Function used for thread
 notification (SIGEV_THREAD) */
 void *sigev_notify_attributes;
 /* Attributes for notification thread
 (SIGEV_THREAD) */
 pid_t sigev_notify_thread_id;
 /* ID of thread to signal (SIGEV_THREAD_ID) */
 };


其中sigev_notify 表示通知方式,有如下几种:

表 3. POSIX Timer 到期通知方式

通知方式

描述

SIGEV_NONE

定时器到期时不产生通知。。。

SIGEV_SIGNAL

定时器到期时将给进程投递一个信号,sigev_signo 可以用来指定使用什么信号。

SIGEV_THREAD

定时器到期时将启动新的线程进行需要的处理

SIGEV_THREAD_ID(仅针对 Linux)

定时器到期时将向指定线程发送信号。

Ø  如果采用 SIGEV_NONE 方式,使用者必须调用timer_gettime 函数主动读取定时器已经走过的时间。类似轮询。

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

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

Ø  SIGEV_THREAD_ID 通常和 SIGEV_SIGNAL 联合使用,这样当 Timer 到期时,系统会向由 sigev_notify_thread_id 指定的线程发送信号,否则可能进程中的任意线程都可能收到该信号。这个选项是 Linux 对 POSIX 标准的扩展,目前主要是 GLibc 在实现 SIGEV_THREAD 的时候使用到,应用程序很少会需要用到这种模式。