当系统切换到高精度模式后,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时间的操做:es6
struct tick_sched {app
struct hrtimer sched_timer; /* 主要用于模拟tick时间的hrtimer。*/less
unsigned long check_clocks; /* 第0位代表是否有符合要求的高精度定时器。*/函数
enum tick_nohz_mode nohz_mode; /* 用于表示当前的工做模式。*/this
......spa
}指针
内核使用函数tick_setup_sched_timer,该函数的做用就是设置一个用于模拟tick事件的hrtimer。rest
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,他们几乎完成了同样的工做。
咱们可使用系统调用timer_create/timer_delete/timer_gettime/timer_settime设置精度达到ns的定时器。不过每一个进程只能有一个。
系统提供getitimer/setitimer系统调用,提供精度为us级别的定时器。