一、前言
timekeeping模块是一个提供时间服务的基础模块。Linux内核提供各种time line,real time clock,monotonic clock、monotonic raw clock等,timekeeping模块就是负责跟踪、维护这些timeline的,并且向其他模块(timer相关模块、用户空间的时间服务等)提供服务,而timekeeping模块维护timeline的基础是基于clocksource模块和tick模块。通过tick模块的tick事件,可以周期性的更新time line,通过clocksource模块、可以获取tick之间更精准的时间信息。
本文熟悉介绍timekeeping的一些基础概念,接着会介绍该模块初始化的过程,此后会从上至下介绍该模块提供的服务、该模块如何和tick模块交互以及如何和clocksource模块交互,最后介绍电源管理相关的内容。
二、timekeeper核心数据定义
1、struct timekeeper数据结构解析
旧的内核定义了很多零散的全局变量来管理linux kernel中的各种系统clock,现在,内核定义的struct timekeeper数据结构来管理各种系统时钟的跟踪以及控制,定义如下:
struct timekeeper {
struct clocksource *clock;------------------------(1)
u32 mult;-----------------------------(2)
u32 shift;
cycle_t cycle_interval; -----------------------(3)
cycle_t cycle_last;
u64 xtime_interval;
s64 xtime_remainder;
u32 raw_interval;
s64 ntp_error;
u32 ntp_error_shift;
u64 xtime_sec;---------------------------(4)
u64 xtime_nsec;
struct timespec wall_to_monotonic; -------------------(5)
ktime_t offs_real;
struct timespec total_sleep_time; ------记录系统睡眠时间
ktime_t offs_boot; ------------记录系统boot time
struct timespec raw_time; -----------------------(6)
s32 tai_offset; ---------------------------(7)
ktime_t offs_tai;
};
(1)timekeeper当前使用的clocksource。这个clock应该系统中最优的那个,如果有好过当前clocksource注册入系统,那么clocksource模块会通知timekeeping模块来切换clocksource。
(2)clock source的cycle值和纳秒转换的facotr,概念和clocksource的mult和shift一致。
(3)NTP相关的成员,这里不详述了,实在是对NTP没有兴趣。
(4)CLOCK_REALTIME类型的系统时钟(其实就是墙上时钟)。我们都知道,时间就像是一条直线(line),不知道起点,也不知道终点,因此我们称之time line。time line有很多种,和如何定义0值的时间以及用什么样的刻度来度量时间相关。人类熟悉的墙上时间和linux kernel中定义的CLOCK_REALTIME都是用来描述time line的,只不过时间原点和如何度量time line上两点距离的刻度不一样。对于人类的时间,0值是耶稣诞生的时间点;对于CLOCK_REALTIME,0值是linux epoch,即1970年1月1日...。对于墙上时间,在度量的时候虽然也是基于秒的,但是人类做了grouping,因此使用了年月日时分秒的概念。这里的秒数是相对与当前分钟值内的秒数。对于linux世界中的CLOCK_REALTIME time,直接使用秒以及纳秒在当前秒内的偏移来表示。
因此,这里xtime_sec用秒这个的刻度单位来度量CLOCK_REALTIME time line上,时间原点到当前点的距离值。当然xtime_sec是一个对current time point的取整值,为了更好的精度,还需要一个纳秒表示的offset,也就是xtime_nsec。
不过为了内核内部计算精度(内核对时间的计算是基于cycle的),并不是保存了时间的纳秒偏移值,而是保存了一个shift之后的值,因此,用户看来,当前时间点的值应该是距离时间原点xtime_sec + (xtime_nsec << shift)距离的那个时间点值
(5)CLOCK_MONOTONIC类型的系统时钟。这种系统时钟并没有象墙上时钟一样定义一个相对于linux epoch的值,这个成员定义了monotonic clock到real time clock的偏移,也就是说,这里的wall_to_monotonic和offs_real需要加上real time clock的时间值才能得到monotonic clock的时间值。当然,从这里成员的名字就看出来了。wall_to_monotonic和offs_real的意思是一样的,不过时间的格式不一样,用在不同的场合,以便获取性能的提升。
(6)CLOCK_MONOTONIC_RAW类型的系统时钟
(7)CLOCK_TAI类型的系统时钟。TAI(international atomic time)是原子钟,在时间的基本概念文档中,我们说过,UTC就是base TAI的,也就是说用铯133的振荡频率来定义秒的那个时钟,当然UTC还有考虑leap second以便方便广大人民群众。CLOCK_TAI类型的系统时钟就是完完全全使用铯133的振荡频率来定义秒的那个时钟,不向人类妥协。
2、全局变量
static struct timekeeper timekeeper;
static DEFINE_RAW_SPINLOCK(timekeeper_lock);
static seqcount_t timekeeper_seq;
static struct timekeeper shadow_timekeeper;
timekeeper维护了系统的所有的clock。一个全局变量(共享资源)没有锁保护怎么行,timekeeper_lock和timekeeper_seq都是用来保护timekeeper的,用在不同