本文简要介绍了Linux实现POSIX定时器的内核代码。内核中对posix定时器的实现代码在kernel\posix-timers.c/h中,本文使用的代码是2.6.29;关于用户空间如何使用POSIX定时器请查阅相关man文档。
Linux提供的POSIX定时器功能主要由以下几个函数组成:
int timer_create(clockid_t which_clock, struct sigevent* timer_event_spec, timer_t* created_timer_id);
int timer_gettime(timer_t timer_id, struct itimerspec* setting);
int timer_settime(timer_t timer_id, int flags, struct itimerspec* new_setting, struct itimerspec* old_setting);
int timer_delete(timer_t timer_id);
除此之外,还有一些附加函数,比如:
clock_nanosleep
clock_getres
clock_settime
clock_gettime
timer_getoverrun
定时器的创建/删除新建定时器的函数是timer_create。
int timer_create(clockid_t which_clock, struct sigevent* timer_event_spec, timer_t* created_timer_id);
l 参数which_clock可以是系统默认的CLOCK_REALTIME,CLOCK_MONOTONIC,或者其它被登记的clock。
l 参数timer_event_spec是定时器到期时的通知方式。在kernel中,timer_event_spec的it_sigev_notify域可以是SIGEV_SIGNAL、SIGEV_NONE、SIGEV_THREAD_ID;但是在用户空间,除上述三个值之外,还有一个SIGEV_THREAD。SIGEV_THREAD比较特殊,是POSIX库在用户空间实现的,与内核无关。www.linuxidc.com这可以在代码的include\asm-generic\siginfo.h中找到说明:
/*
* sigevent definitions
*
* It seems likely that SIGEV_THREAD will have to be handled from
* userspace, libpthread transmuting it to SIGEV_SIGNAL, which the
* thread manager then catches and does the appropriate nonsense.
* However, everything is written out here so as to not get lost.
*/
#define SIGEV_SIGNAL 0 /* notify via signal */
#define SIGEV_NONE 1 /* other notification: meaningless */
#define SIGEV_THREAD 2 /* deliver via thread creation */
#define SIGEV_THREAD_ID 4 /* deliver to thread */
另外,SIGEV_THREAD_ID被用于实现POSIX线程库,应用程序也不能随意使用。(见附注)
l 参数created_timer_id是创建定时器成功后,返回的定时器编号。
下面看一下新建定时器的具体实现:
int timer_create(clockid_t which_clock, struct sigevent* timer_event_spec, timer_t* created_timer_id);
struct k_itimer * new_timer = alloc_posix_timer(); // 分配定时器占用的内存
。。。 // 调用idr模块将new_timer与一个id关联,并设置相关域
// 下面的COCK_DISPATCH宏默认调用到common_timer_create函数
CLOCK_DISPATCH(which_clock, timer_create, (new_timer));
。。。 // 将struct sigevent* timer_event_spec保存到new_timer的相应结构中,这里有 // 用户空间和内核空间的转换
。。。 // 将new_timer挂入struct task_struct::signal->posix_timers中。这里如果指定 // 了it_sigev_notify域为SIGEV_THREAD_ID,那么task_struct为定时器到期 // 时需要通知的线程;否则,task_struct为当前线程所属进程
// posix_timers队列维护一个task中所有已使用的定时器链表,该链表用于进 // 程退出时删除所有已使用的posix定时器
common_timer_create函数初始化k_itimer中的hrtimer域,hrtimer是Linux的高精度时钟
static int common_timer_create(struct k_itimer *new_timer)
{
hrtimer_init(&new_timer->it.real.timer, new_timer->it_clock, 0);
return 0;
}
定时器的删除操作与新建定时器对应,释放定时器建立时分配的资源。
定时器的启动
int timer_settime(timer_t timer_id, int flags, struct itimerspec* new_setting, struct itimerspec* old_setting);
l 参数timer_id是timer_create返回的定时器id号。
l 参数flags为TIMER_ABSTIME或者0,代表设置的超时值是绝对还是相对时间
l 参数new_setting是需要被设置的超时时间
l 参数old_setting用于返回设置新超时值之前定时器的超时时间
timer_setting的功能主要由宏
CLOCK_DISPATCH(timr->it_clock, timer_set, (timr, flags, &new_spec, rtn));
完成,该宏默认情况下会调用common_timer_set。
int common_timer_set(struct k_itimer *timr, int flags,
struct itimerspec *new_setting, struct itimerspec *old_setting)
common_timer_set中,会设置timr参数中包含的hrtimer(struct k_itimer:: it.real.timer),通过hrtimer机制完成定时器的超时设置,hrtimer的超时函数被设置为posix_timer_fn。
static enum hrtimer_restart posix_timer_fn(struct hrtimer *timer)
。。。
// 如果定时器处于循环模式,每一次到期时加一
if (timr->it.real.interval.tv64 != 0)
si_private = ++timr->it_requeue_pending;
// 调用posix_timer_event发送定时器超时消息
// 如果定时器设置的是SIGEV_NONE且处于循环模式,则posix_timer_event返回值为 // 非0。这时定时器的循环设置将在这个超时函数中完成(返回值设为 // HRTIMER_RESTART可使hrtimer模块重启定时器)。
if (posix_timer_event(timr, si_private))
if (timr->it.real.interval.tv64 != 0)
hrtimer_forward (timer, now, timr->it.real.interval);
ret = HRTIMER_RESTART;
在定时器没有设置SIGEV_NONE时,超时函数posix_timer_event将不会重启处于循环模式的定时器,重启定时器的工作会在signal处理的函数中。
posix_timer_event的主要功能是填充sigqueue结构,并发送到相应的sigpending(Linux的signal机制)队列。
int posix_timer_event(struct k_itimer *timr, int si_private)
。。
imr->sigq->info.si_code = SI_TIMER; // SI_TIMER比较重要,下文会提到
。。。
if (timr->it_sigev_notify & SIGEV_THREAD_ID) {
struct task_struct *leader;
// 注意这里最后一个参数是0,代表发给线程
int ret = send_sigqueue(timr->sigq, timr->it_process, 0);
if (likely(ret >= 0)) // 如果成功发送,则返回;否则发给该线程所属的进程
return ret;
timr->it_sigev_notify = SIGEV_SIGNAL;
leader = timr->it_process->group_leader;
put_task_struct(timr->it_process);
timr->it_process = leader;
}
// 将信号发给进程(最后一个参数为1)
return send_sigqueue(timr->sigq, timr->it_process, 1);
send_sigqueue是Linux的signal机制实现的函数,但该函数中有一个小细节与timer相关。
// 下面条件判断定时器超时消息是否已经在sigpending队列中,如果已经在,说明上一 // 次超时还没有处理,下一次定时器超时就已经再次到达。这时只增加sigqueue的 // si_overrun标记位以标记定时器超时溢出,而不会再一次排队sigqueue
if (unlikely(!list_empty(&q->list))) {
/*
* If an SI_TIMER entry is already queue just increment
* the overrun count.
*/
BUG_ON(q->info.si_code != SI_TIMER);
q->info.si_overrun++;
goto out;
}
q->info.si_overrun = 0;
// 将sigqueue排入sigpending队列
。。。
out:
unlock_task_sighand(t, &flags);
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-03/33641.htm