linux时间子系统(九)

3.4.3 模拟tick事件

     当系统切换到高精度模式后,tick_device被高精度定时器系统接管,不再定期地产生tick事件。内核在3.0.30版本中还没有彻底的废除jiffies机制,系统还是依赖定期到来的tick事件,完成进程调度和时间更新等操作,大量存在的低精度定时器仍然依赖于jiffies计数。所以,尽管tick_device被接管,高精度定时器系统仍然需要继续提供定期的tick事件。为了完成这个需求,由于高精度模式已经启用,内核定义了一个hrtimer,把它的到期时间设定为一个jiffy的时间,当着个hrtimer到期时,在这个hrtimer的到期函数中,进行和原来的tick_device同样的操作,然后把该hrtimer的到期时间顺延一个jiffy周期。如此反复循环,可以完美的模拟原有tick_device的功能。
     在kernel/time/tick-sched.c中,内核定义了一个per_cpu的全局变量:tick_cpu_sched,从而为每个cpu提供了一个tick_sched结构。该结构主要用于管理NO_HZ配置下的tickless处理,因为模拟tick事件与tickless有很强的相关性,所以高精度定时器也利用了该结构的以下字段,用来完成模拟tick时间的操作:

struct tick_sched {

        struct hrtimer                  sched_timer; /* 主要用于模拟tick时间的hrtimer。*/

        unsigned long                   check_clocks; /* 第0位表明是否有符合要求的高精度定时器。*/

        enum tick_nohz_mode             nohz_mode; /* 用于表示当前的工作模式。*/

......

}

   内核使用函数tick_setup_sched_timer,该函数的作用就是设置一个用于模拟tick事件的hrtimer。

void tick_setup_sched_timer(void)

{

        struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);

        ktime_t now = ktime_get();

        /*

         * Emulate tick processing via per-CPU hrtimers:

         */

        hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); /* 初始化该cpu所属的tick_sched结构中的sched_timer字段。*/

        ts->sched_timer.irqsafe = 1;

        ts->sched_timer.function = tick_sched_timer; /* 把该hrtimer的回调函数设置为tick_sched_timer.*/

        /* Get the next period (per cpu) */

        hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update()); /* 设置到期时间为下一个jiffy时刻。*/

        for (;;) {

                hrtimer_forward(&ts->sched_timer, now, tick_period);

                hrtimer_start_expires(&ts->sched_timer,

                                      HRTIMER_MODE_ABS_PINNED);

                /* Check, if the timer was already in the past */

                if (hrtimer_active(&ts->sched_timer))

                        break;

                now = ktime_get();

        }

#ifdef CONFIG_NO_HZ

        if (tick_nohz_enabled) {

                ts->nohz_mode = NOHZ_MODE_HIGHRES; /* 将工作模式设置为NOHZ_MODE_HIGHRES模式,表明利用高精度模式实现NO_HZ。*/

                printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id());

        }

#endif

}

static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)

{               

        struct tick_sched *ts =

                container_of(timer, struct tick_sched, sched_timer);

        struct pt_regs *regs = get_irq_regs();

        ktime_t now = ktime_get();

        int cpu = smp_processor_id();

        static int i=0;

#ifdef CONFIG_NO_HZ

        /*

         * Check if the do_timer duty was dropped. We don't care about

         * concurrency: This happens only when the cpu in charge went

         * into a long sleep. If two cpus happen to assign themself to

         * this duty, then the jiffies update is still serialized by

         * xtime_lock.

         */

        if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))

                tick_do_timer_cpu = cpu;

#endif

        /* Check, if the jiffies need an update */

        if (tick_do_timer_cpu == cpu) /* 在smp系统中,只有一个cpu负责jiffies计数,时间更新等全局操作。所以判定当前cpu

   是否是负责更新jiffies和时间,如果是,则执行更新操作。*/

                tick_do_update_jiffies64(now);

        /*

         * Do not call, when we are not in irq context and have

         * no valid regs pointer

         */

        if (regs) { /* 利用regs指针确保当前是在中断上下文,然后调用update_precess_timer。*/

                /*

                 * When we are idle and the tick is stopped, we have to touch

                 * the watchdog as we might not schedule for a really long

                 * time. This happens on complete idle SMP systems while

                 * waiting on the login prompt. We also increment the "start of

                 * idle" jiffy stamp so the idle accounting adjustment we do

                 * when we go busy again does not account too much ticks.

                 */

                if (ts->tick_stopped) {

                        touch_softlockup_watchdog();

                        ts->idle_jiffies++;

                }

                update_process_times(user_mode(regs));

                profile_tick(CPU_PROFILING);

        }

        hrtimer_forward(timer, now, tick_period); /* 把hrtimer的到期时间推进一个tick周期。*/

        return HRTIMER_RESTART; /*返回HRTIMER_RESTART表明该hrtimer需要再次启动,以便产生下一个tick事件。*/

}

     对比模拟tick时间的hrtimer的回调函数tick_sched_timer和切换前tick_device的回调函数tick_handle_periodic,他们几乎完成了一样的工作。

3.5 hrtimer的使用

     我们可以使用系统调用timer_create/timer_delete/timer_gettime/timer_settime设置精度达到ns的定时器。不过每个进程只能有一个。

     系统提供getitimer/setitimer系统调用,提供精度为us级别的定时器。

猜你喜欢

转载自blog.csdn.net/kylinos_123/article/details/81511929