新建定时器的函数是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这可以在代码的includeasm-genericsiginfo.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);