如果内核是有生命的话,那么时间就是内核的心脏,控制着内核的脉搏,但是这颗心脏跳动的方式根据硬件的配置会有不同的跳动方式。内核中有大量的需求需要时间的帮助,比如:定时、进程调度、获得时间等等,在内核中时间子系统就是来实现这部分功能的,根据不同的工作模式(periodic和oneshot)会有不同的工作函数来实现周期性任务,具体分为低精度模式和高精度模式。
tick_handle_periodic函数的调用图如下:
此函数主要是更新jiffies_64、计算负载、更新墙上时间也就是系统时间,由于是工作在periodic模式,所以每次执行完毕,没必要reprogram下一次的event。
下面再介绍一下update_process_times
A : 从periodic转换到oneshot模式(动态模式),分为低精度和高精度
B:调度相关操作
C:posix timer相关操作
在高精度模式下,时钟的轮转都是动态的,所以要执行周期性的任务需要基于动态时钟来模拟周期时钟,高精度时钟是基于hrtimer实现的,所以周期性的任务被初始化为一个hrtimer并被注册。
A : 更换要调用的函数为hrtimer_interrupt
B : 设置周期性的hrtimer
hrtimer_interrupt做的主要工作是:
1.得到此时的time_now
2.运行hrtimer队列中的任务
3.设置下一次中断的时间
__hrtimer_run_queues主要从队列中得到合适的函数来执行
(kernel/time/hrtimer.c)
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
{
struct hrtimer_clock_base *base = cpu_base->clock_base;
unsigned int active = cpu_base->active_bases;
for (; active; base++, active >>= 1) {
struct timerqueue_node *node;
ktime_t basenow;
if (!(active & 0x01))
continue;
basenow = ktime_add(now, base->offset);
while ((node = timerqueue_getnext(&base->active))) {--------------得到下一个hrtimer
struct hrtimer *timer;
timer = container_of(node, struct hrtimer, node);
/*
* The immediate goal for using the softexpires is
* minimizing wakeups, not running timers at the
* earliest interrupt after their soft expiration.
* This allows us to avoid using a Priority Search
* Tree, which can answer a stabbing querry for
* overlapping intervals and instead use the simple
* BST we already have.
* We don't add extra wakeups by delaying timers that
* are right-of a not yet expired timer, because that
* timer will have to trigger a wakeup anyway.
*/
if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer))
break;
__run_hrtimer(cpu_base, base, timer, &basenow);-------------运行hrtimer
}
}
}
不过有一点还没说清楚,hrtimer_interrupt是和tick_handle_periodic一样执行周期任务的,但是从hrtimer_interrupt中只能看到它会调用相关的function,没有看到tick_periodic相关的操作。这是因为在切换到oneshot的时候,已经把相关的hrtimer注册到其中了,具体下节2.2介绍
在1.2节中对于模式的切换有一个函数没有太多介绍,这个函数就是tick_setup_sched_timer,它的主要工作就是完成执行类似tick_periodic相关操作函数的初始化,把其加入到hrtimer_cpu_base中
( kernel/time/tick-sched.c )
void tick_setup_sched_timer(void)
{
struct tick_sched *ts = this_cpu_ptr(&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);--把sched_timer这个hrtimer加入到hrtimer_cpu_base中
ts->sched_timer.function = tick_sched_timer;------------------------周期性调用的函数(类似tick_periodic)
/* Get the next period (per cpu) */
hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
/* Offset the tick to avert jiffies_lock contention. */
if (sched_skew_tick) {
u64 offset = ktime_to_ns(tick_period) >> 1;
do_div(offset, num_possible_cpus());
offset *= smp_processor_id();
hrtimer_add_expires_ns(&ts->sched_timer, offset);
}
hrtimer_forward(&ts->sched_timer, now, tick_period);
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
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();
tick_sched_do_timer(now);----------------------更新jiffies64
/*
* Do not call, when we are not in irq context and have
* no valid regs pointer
*/
if (regs)
tick_sched_handle(ts, regs);
/* No need to reprogram if we are in idle or full dynticks mode */
if (unlikely(ts->tick_stopped))
return HRTIMER_NORESTART;
hrtimer_forward(timer, now, tick_period);-----
return HRTIMER_RESTART;
}
date | content | linux |
---|---|---|
2016.12.25 | (圣诞节还在写博客) | linux4.6.3 |
2017.1.2 | 分清高精度和低精度周期任务 | linux4.6.3 |