當系統切換到高精度模式後,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級別的定時器。