1. Kernel Timer 软件架构

| 术语 | 说明 | 
|---|---|
| Global Counter | free running system counter, 可以参看arm_arm 手册里的Generic Timer->system counter。Rollover 时间至少40年。他提供了一个基础的timeline, 无线延伸 | 
| CPU local Timer | CPU local timer 可以是Peripheral HW Timer, 或者是CPU 如ARM 含有的Generic Timer -> timer. | 
| clock event | 通过timer硬件的 中断处理函数 完成的,在此基础上可以构建tick模块。clock event 是在timeline 上指定点产生event。 | 
| tick 模块 | 维护了系统的tick,各个 进程的时间统计 也是基于tick的,内核的 调度器 根据这些信息进行调度。System Load和Kernel Profiling模块 也是基于tick的,用于计算系统负荷和进行内核性能剖析。 | 
| timekeeping模块 | 系统时间, 每tick 的发生,其值增加。高进度的值,可以来源于clocksource | 
| timer lib | 用户空间需求:1.获取系统时间,time, stime, gettimeofday, 2.定时器功能,settimer, alarm等 | 
一个CPU 可以有多个local Clock Event, 但是会选择一个适合的作为tick device。
tick device 工作模式:
- one shot mode(提供高精度的clock event)
- periodic mode
一般有多少个cpu,就会有多少个tick device - local tick device, 在所有device 中会选取一个做global tick device, 负责维护整个系统的jiffies,更新wall clock,计算全局负荷等。
当系统处于高精度timer的时候(tick device处于one shot mode),系统会setup一个特别的高精度timer(可以称之sched timer),该高精度timer会周期性的触发,从而模拟的传统的periodic tick,从而推动了传统低精度timer的运转。因此,一些传统的内核模块仍然可以调用经典的低精度timer模块的接口。

2. file structure
| 文件 | 说明 | 
|---|---|
| time.c timeconv.c | 用户空间函数,time, stime, gettimeofday,alarm等,以及转换函数 | 
| time_list.c time_status.c | 向用户空间提供的调试接口。在用户空间,可以通过/proc/timer_list接口可以获得内核中的时间子系统的相关信息。 | 
| posix-timer.c posix-cpu-timers.c posix-clock.c | POSIX timer, clock模块 | 
| alrmtimer.c | alarmtimer 模块 | 
| ntp.c | NTP 模块 | 
| timerkeeping.c timerkeeping_debug.c | timerkeeping.c模块 | 
| ick-common.c tick-oneshot.c tick-sched.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模块。 | 
| sched_clock.c | 通用sched clock模块。这个模块主要是提供一个sched_clock的接口函数,可以获取当前时间点到系统启动之间的纳秒值。底层的HW counter其实是千差万别的,有些平台可以提供64-bit的HW counter,我们可以不使用这个通用sched clock模块(不配置CONFIG_GENERIC_SCHED_CLOCK这个内核选项),而在自己的clock source chip driver中直接提供sched_clock接口。使用通用sched clock模块的好处是:该模块扩展了64-bit的counter,即使底层的HW counter比特数目不足(有些平台HW counter只有32个bit)。 | 
| clocksource.c jiffies.c | clocksource.c是通用clocksource driver。其实也可以把system tick也看成一个特定的clocksource,其代码在jiffies.c文件中 | 
| clockevnet.c | clockevent 模块 | 
| timer.c | 传统的低精度timer 模块, 基本tick | 
| htimer.c | 高精度timer | 
3. clocksource
时间其实可以抽象成一条直线, timeline. clock source就是用来抽象一个在指定输入频率的clock下工作的一个counter。输入频率可以确定以什么样的精度来划分timeline(假设输入counter的频率是1GHz,那么一个cycle就是1ns)
clocksource 数据结构如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78/* linux-4.9.198 code */
/**
 * struct clocksource - hardware abstraction for a free running counter
 *	Provides mostly state-free accessors to the underlying hardware.
 *	This is the structure used for system time.
 *
 * @name:		ptr to clocksource name
 * @list:		list head for registration
 * @rating:		rating value for selection (higher is better)
 *			To avoid rating inflation the following
 *			list should give you a guide as to how
 *			to assign your clocksource a rating
 *			1-99: Unfit for real use
 *				Only available for bootup and testing purposes.
 *			100-199: Base level usability.
 *				Functional for real use, but not desired.
 *			200-299: Good.
 *				A correct and usable clocksource.
 *			300-399: Desired.
 *				A reasonably fast and accurate clocksource.
 *			400-499: Perfect
 *				The ideal clocksource. A must-use where
 *				available.
 * @read:		returns a cycle value, passes clocksource as argument
 * @enable:		optional function to enable the clocksource
 * @disable:		optional function to disable the clocksource
 * @mask:		bitmask for two's complement
 *			subtraction of non 64 bit counters
 * @mult:		cycle to nanosecond multiplier
 * @shift:		cycle to nanosecond divisor (power of two)
 * @max_idle_ns:	max idle time permitted by the clocksource (nsecs)
 * @maxadj:		maximum adjustment value to mult (~11%)
 * @max_cycles:		maximum safe cycle value which won't overflow on multiplication
 * @flags:		flags describing special properties
 * @archdata:		arch-specific data
 * @suspend:		suspend function for the clocksource, if necessary
 * @resume:		resume function for the clocksource, if necessary
 * @owner:		module reference, must be set by clocksource in modules
 *
 * Note: This struct is not used in hotpathes of the timekeeping code
 * because the timekeeper caches the hot path fields in its own data
 * structure, so no line cache alignment is required,
 *
 * The pointer to the clocksource itself is handed to the read
 * callback. If you need extra information there you can wrap struct
 * clocksource into your own struct. Depending on the amount of
 * information you need you should consider to cache line align that
 * structure.
 */
struct clocksource {
	cycle_t (*read)(struct clocksource *cs);
	cycle_t mask;
	u32 mult;
	u32 shift;
	u64 max_idle_ns;
	u32 maxadj;
	struct arch_clocksource_data archdata;
	u64 max_cycles;
	const char *name;
	struct list_head list;
	int rating;
	int (*enable)(struct clocksource *cs);
	void (*disable)(struct clocksource *cs);
	unsigned long flags;
	void (*suspend)(struct clocksource *cs);
	void (*resume)(struct clocksource *cs);
	/* private: */
	/* Watchdog related data, used by the framework */
	struct list_head wd_list;
	cycle_t cs_last;
	cycle_t wd_last;
	struct module *owner;
};
3.1. register/unregister clocksource
系统中使用cycle_t cycles 进行计数, 为了人们的方便会转换成年月日方式,所以就会用mult, shift(乘法,除法系数)进行转换。
| 1 | static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift) | 
| 1 | /* need to calculate mult, shift by caller */ | 
3.2. OS选择clock source
主要参看的因素有两个:
- best rate (分辨率越高的)
- 用户空间的选择1 static void __clocksource_select(bool skipcur) 
3.3. timecounter 与 cyclecounter
具体来讲, cyclecounter 表示free running system counter 绝对的时间点, 而timecounter 表示counter, 但是表达的是ns 时间单位。
| 1 | /** | 
4. clockevent
clockevent 数据结构如下
| 1 | /** | 
上面重要的有1
2
3
4
5
6
7   /* set next event interrupt by cyclecounter or ktime */
   int			(*set_next_event)(unsigned long evt, struct clock_event_device *);
int			(*set_next_ktime)(ktime_t expires, struct clock_event_device *);
   /* switch event mode: periodic or oneshot */
int			(*set_state_periodic)(struct clock_event_device *);
int			(*set_state_oneshot)(struct clock_event_device *);
4.1. 常用函数
| 1 | extern void clockevents_config_and_register(struct clock_event_device *dev, | 
在我们register clockevnet 后, 上层的tick layer 可能会考虑替换当前的clockevent device.
替换参考的依据是:
- 首先是CPU local device
- 具备更好的rate
如果current clockevent device 是broadcast device(broadcost clockevent device 主要用于在CPU sleep 时,其他CPU local timer 已经睡眠,在resume时我们可以通过broadcast device 唤醒其他CPU),需要先close.
| 1 | /* linux-4.9.198 code */ |