一、时间子系统的软件架构
二、tick 层模块的文件
tick-common.c
tick-oneshot.c
tick-sched.c
tick-broadcast.c
tick-broadcast-hrtimer.c
这三个文件属于tick device layer。
tick-common.c文件是periodic tick模块,用于管理周期性tick事件。
tick-oneshot.c文件是for高精度timer的,用于管理高精度tick时间。
tick-sched.c是用于dynamic tick的。
tick-broadcast.c 和 tick-broadcast-hrtimer.c 是broadcast tick模块。
如果说每个.c文件是一个模块的话,我们可以首先简单描述tick device layer的各个模块。tick-common.c描述了tick device的一些通用操作,此外,该文件还包括了周期性tick的代码。想要让系统工作在tickless mode(更准确应该是Dynamic tick模块,也就是说根据系统的当前运行状况,动态的启停周期性tick)需要两个模块的支持,分别是tick-oneshot.c和tick-sched.c。tick-oneshot.c主要是提供和tick device的one shot mode相关的操作接口函数。从字面上看,tick-sched.c是和tick的调度相关,所谓tick的调度包括两个方面,一方面是在系统正常运行过程中,如何产生周期性的tick event,另一方面是在系统没有任务执行,进入idle状态的时候,如何停止周期性的tick,以及恢复的时候如何更新系统状态(例如:jiffies等)。tick-broadcast.c和tick-broadcast-hrtimer.c是和tick broadcast相关,本文不会涉及这部分的内容,会有专门的文档描述它
1、什么是tick
想要理解什么是tick device,什么是tickless kernel,首先当然要理解什么是tick?要理解什么是tick,首先要理解OS kernel是如何运作的。系统中有很多日常性的事情需要处理,例如:
---更新系统时间
---处理低精度timer
---处理正在运行进程的时间片信息
系统在处理这些事情的时候使用了轮询的方式,也就是说按照固定的频率去做这些操作。这时候就需要HW的协助,一般而言,硬件会有HW timer(称之system timer)可以周期性的trigger interrupt,让系统去处理上述的日常性事务。每次timer中断到来的时候,内核的各个模块就知道,一个固定的时间片已经过去。对于日常生活,tick这个概念是和钟表关联的:钟表会发出周期性的滴答的声音,这个声音被称为tick。CPU和OS kernel扩展了这个概念:周期性产生的timer中断事件被称为tick,而能够产生tick的设备就称为tick device。
如何选择tick的周期是需要在power comsuption、时间精度以及系统响应时间上进行平衡。我们考虑系统中基于tick的低精度timer模块,选择较高的tick频率会提高时间精度,例如对于,10ms的tick周期意味着低精度timer的时间精度就是10ms,设定3ms的低精度timer没有任何意义。为了提高时间精度,我们可以提高tick的频率,例如可以提升到1ms的tick,但是,这时更多的CPU的时间被花费在timer的中断处理,实际上,当系统不繁忙的时候,并不是每一个tick都是那么有意义,实际上大部分的tick到来的时候,OS kernel往往只是空转,实际上并有什么事情做,这对系统的power consumption是有害的。对于嵌入式设备,周期性的tick对power consumption危害更大,因为对于嵌入式设备,待机时间是一个很重要的指标,而周期性tick则意味着系统不可能真正的进入idle状态,而是会周期性的被wakeup,这些动作会吃掉电池的电量。同理,对于调度器而言亦然。如果设定10ms的tick,分配每个进程的时间片精度只是10ms,调度器计算每个进程占用CPU的时间也只能是以10ms为单位。为了提高进程时间片精度,我们可以提高tick的频率,例如可以提升到1ms的tick,但是,这时更多的CPU的时间被花费在进程上下文的切换上,但是,对应的好处是系统的响应时间会更短。
三、periodic tick 模块
1、数据结构
在内核中,使用struct tick_device来抽象系统中的tick设备,如下:
struct tick_device {
struct clock_event_device *evtdev;
enum tick_device_mode mode;
};
从上面的定义就可以看出:所谓tick device其实就是工作在某种模式下的clock event设备。工作模式体现在tick device的mode成员,evtdev指向了和该tick device关联的clock event设备。
tick device的工作模式定义如下:
enum tick_device_mode {
TICKDEV_MODE_PERIODIC,
TICKDEV_MODE_ONESHOT,
};
tick device可以工作在两种模式下,一种是周期性tick模式,另外一种是one shot模式。one shot模式主要和tickless系统以及高精度timer有关。
2、tick device的分类以及和CPU的关系
(1) local tick device。在单核系统中,传统的unix都是在tick驱动下进行任务调度、低精度timer触发等,在多核架构下,系统为每一个cpu建立了一个tick device,如下:
DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
local tick device的clock event device应该具备下面的特点:
(a)该clock event device对应的HW timer必须是和该CPU core是有关联的的(也就是说,该hw timer的中断是可以送达到该CPU core的)。struct clock_event_device 有一个cpumask成员,它可以指示该clock event device为哪一个或者哪几个CPU core工作。如果采用ARM generic timer的硬件,其HW timer总是为一个CPU core服务的,我们称之为per cpu timer。
(b)该clock event device支持one shot模式,并且精度最高(rating最大)
(2)global tick device。具体定义如下:
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;
有些任务不适合在local tick device中处理,例如更新jiffies,更新系统的wall time,更新系统的平均负载(不是单一CPU core的负载),这些都是系统级别的任务,只需要在local tick device中选择一个作为global tick device就OK了。tick_do_timer_cpu指明哪一个cpu上的local tick作为global tick。
(3)broadcast tick device,定义如下:
static struct tick_device tick_broadcast_device;
我们会单独一份文档描述它,这里就不再描述了。
四、初始化tick device
1、注册一个新的clock event device的时候,tick device layer要做什么?
在clock_event的文章中,我们知道:底层的timer硬件驱动在初始化的时候会注册clock event device,在注册过程中就会调用tick_check_new_device函数来看看是否需要进行tick device的初始化,如果已经已经初始化OK的tick device是否有更换更高精度clock event device的需求。代码如下:
void tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu;cpu = smp_processor_id();-----------------(1)
td = &per_cpu(tick_cpu_device, cpu);---获取当前cpu的tick device
curdev = td->evtdev; ---目前tick device正在使用的clock event deviceif (!tick_check_percpu(curdev, newdev, cpu))---------(2)
goto out_bc;if (!tick_check_preferred(curdev, newdev))-----------(3)
goto out_bc;if (!try_module_get(newdev->owner)) -----增加新设备的reference count
return;
if (tick_is_broadcast_device(curdev)) { -------------(4)
clockevents_shutdown(curdev);
curdev = NULL;
}
clockevents_exchange_device(curdev, newdev); ---通知clockevent layer
tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); --------(5)
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
return;out_bc:
tick_install_broadcast_device(newdev); ----其他文档中描述
}
(1)是否是为本CPU服务的clock event device?如果不是,那么不需要考虑per cpu tick device的初始化或者更换该cpu tick device的clock event devic