Linux Posix Timer实现代码分析


本文简要介绍了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_REALTIMECLOCK_MONOTONIC或者其它被登记的clock

l         参数timer_event_spec是定时器到期时的通知方式。在kernel中,timer_event_specit_sigev_notify域可以是SIGEV_SIGNALSIGEV_NONESIGEV_THREAD_ID;但是在用户空间,除上述三个值之外,还有一个SIGEV_THREADSIGEV_THREAD比较特殊,是POSIX库在用户空间实现的,与内核无关。www.bkjia.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域,hrtimerLinux的高精度时钟

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_idtimer_create返回的定时器id号。

l         参数flagsTIMER_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结构,并发送到相应的sigpendingLinuxsignal机制)队列。

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_sigqueueLinuxsignal机制实现的函数,但该函数中有一个小细节与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);

  • 1
  • 2
  • 下一页

相关内容