typedef u64 cycle_t; cycle_t 是从时钟源设备中读取的时钟类型。 为了管理这些不同的时间结构,Linux 实现了一系列辅助函数来完成相互间的转换。 ktime_to_timespec,ktime_to_timeval,ktime_to_ns/ktime_to_us,反过来有诸如 ns_to_ktime 等类似的函数。 timeval_to_ns,timespec_to_ns,反过来有诸如 ns_to_timeval 等类似的函数。 timeval_to_jiffies,timespec_to_jiffies,msecs_to_jiffies, usecs_to_jiffies, clock_t_to_jiffies 反过来有诸如 ns_to_timeval 等类似的函数。 clocksource_cyc2ns / cyclecounter_cyc2ns 有了以上的介绍,通过图 3 可以更加清晰的看到这几者之间的关联。 图 2. 内核时钟子系统的结构关系 时钟源设备和时钟事件设备的引入,将原本放在各个体系结构中重复实现的冗余代码封装到各自的抽象层中,这样做不但消除了原来 timer wheel 与内核其他模块的紧耦合性,更重要的是系统可以在运行状态动态更换时钟源设备和时钟事件设备而不影响系统正常使用,譬如当 CPU 由于睡眠要关闭当前使用的时钟源设备或者时钟事件设备时系统可以平滑的切换到其他仍处于工作状态的设备上。Timekeeping/GTOD 在使用时钟源设备的基础上也采用类似的封装实现了体系结构的无关性和通用性。hrtimer 则可以通过 timekeeping 提供的接口完成定时器的更新,通过时钟事件设备提供的事件机制,完成对 timer 的管理。在图 3 中还有一个重要的模块就是 tick device 的抽象,尤其是 dynamic tick。Dynamic tick 的出现是为了能在系统空闲时通过停止 tick 的运行以达到降低 CPU 功耗的目的。使用 dynamic tick 的系统,只有在有实际工作时才会产生 tick,否则 tick 是处于停止状态。下文会有专门的章节进行论述。 hrtimer 的实现机制 hrtimer 是建立在 per-CPU 时钟事件设备上的,对于一个 SMP 系统,如果只有全局的时钟事件设备,hrtimer 无法工作。因为如果没有 per-CPU 时钟事件设备,时钟中断发生时系统必须产生必要的 IPI 中断来通知其他 CPU 完成相应的工作,而过多的 IPI 中断会带来很大的系统开销,这样会令使用 hrtimer 的代价太大,不如不用。为了支持 hrtimer,内核需要配置 CONFIG_HIGH_RES_TIMERS=y。hrtimer 有两种工作模式:低精度模式(low-resolution mode)与高精度模式(high-resolution mode)。虽然 hrtimer 子系统是为高精度的 timer 准备的,但是系统可能在运行过程中动态切换到不同精度的时钟源设备,因此,hrtimer 必须能够在低精度模式与高精度模式下自由切换。由于低精度模式是建立在高精度模式之上的,因此即便系统只支持低精度模式,部分支持高精度模式的代码仍然会编译到内核当中。 在低精度模式下,hrtimer 的核心处理函数是 hrtimer_run_queues,每一次 tick 中断都要执行一次。如清单 3 所示。这个函数的调用流程为: update_process_times run_local_timers hrtimer_run_queues raise_softirq(TIMER_SOFTIRQ)
清单 3. 低精度模式下 hrtimer 的核心处理函数
void hrtimer_run_queues(void) { struct rb_node *node; struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); struct hrtimer_clock_base *base; int index, gettime = 1; if (hrtimer_hres_active()) return; for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { base = &cpu_base->clock_base[index]; if (!base->first) continue; if (gettime) { hrtimer_get_softirq_time(cpu_base); gettime = 0; } raw_spin_lock(&cpu_base->lock); while ((node = base->first)) { struct hrtimer *timer; timer = rb_entry(node, struct hrtimer, node); if (base->softirq_time.tv64 <= hrtimer_get_expires_tv64(timer)) break; __run_hrtimer(timer, &base->softirq_time); } raw_spin_unlock(&cpu_base->lock); } } | | |