[linux]linux核心時間管理基礎
一,linux時間管理基礎
http://blog.csdn.net/droidphone/article/details/7975694
http://blog.csdn.net/smilingjames/article/details/6205540
linux所有時間基礎都是以低層硬體為基礎的,低層硬體有GPT和cpu local timer,比如GPT的時鐘源為13M HZlinux低層時間的架構分為clock source,clock event device,clock source上層分為Xtimer和Hrtimer,Xtimer主要是指牆上時間(開機的時候從RTC暫存器讀取牆上時間),Hrtimer主要是高精度的計時器,精度可以到ns級別,Clock event Device向上層提供jiffies以及時間輪的概念,比如程序切換的最小精度是10ms。
struct clocksource,定義了一個clock device的基本屬性及行為, 這些clock device一般都有計數,定時, 產生中斷能力, 比如GPT
struct clock_event_device Clock event的主要作用是分發clock事件及設定下一次觸發條件. 在沒有clock event之前,時鐘中斷都是週期性地產生, 也就是熟知的jiffies和HZ
二:jiffies和HZ的概念
在ARM系統上HZ的大小一般為100,表示1s內有100個節拍,jiffies表示的是系統自從啟動以來的節拍總數,jiffies一般為unsigned long型別,所以可能會溢位。比如:unsigned long jiffies; unsigned long timeout=jiffies+HZ/2;表示的是未來的0.5s
jiffies迴繞的問題:
01.unsigned long jiffies;
02.unsigned long timeout = jiffies + HZ/2;
03.//......
04.if (timeout > jiffies) {
05. //沒有超時,很好
06.}
07.else {
08. //超時了,發生錯誤
09.}
其中jiffies是個不斷在增大的unsigned long,timeout可以看作比jiffies“大不了多少”的unsigned long。當jiffies變得比2^32-1還要大的時候會發生溢位,“迴繞”(wrap around)到0附近。此時,判斷語句為真,雖然實際上超時了,但是判斷為沒有超時。Linux核心提供了一組巨集解決這個問題。其中巨集time_after(a, b)是考慮可能的溢位情況後判斷時間a是否在時間b之後(即“b < a”)。
01.#define time_after(a, b) ((long)(b) - (long)(a) < 0)
三,Clock Source Struct
1. struct clocksource {
6. cycle_t (*read)(struct clocksource *cs);
7. cycle_t cycle_last;
8. cycle_t mask;
9. u32 mult;
10. u32 shift;
11. u64 max_idle_ns;
12. u32 maxadj;
13. #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
14. struct arch_clocksource_data archdata;
15. #endif
17. const char *name;
18. struct list_head list;
19. int rating;
20. int (*enable)(struct clocksource *cs);
21. void (*disable)(struct clocksource *cs);
22. unsigned long flags;
23. void (*suspend)(struct clocksource *cs);
24. void (*resume)(struct clocksource *cs);
25.
26. /* private: */
27. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
28. /* Watchdog related data, used by the framework */
29. struct list_head wd_list;
30. cycle_t cs_last;
31. cycle_t wd_last;
32. #endif
33. } ____cacheline_aligned;
1 rating:時鐘源的精度
同一個裝置下,可以有多個時鐘源,每個時鐘源的精度由驅動它的時鐘頻率決定,比如一個由10MHz時鐘驅動的時鐘源,他的精度就是100nS。clocksource結構中有一個rating欄位,代表著該時鐘源的精度範圍,它的取值範圍如下:
1--99: 不適合於用作實際的時鐘源,只用於啟動過程或用於測試;
100--199:基本可用,可用作真實的時鐘源,但不推薦;
200--299:精度較好,可用作真實的時鐘源;
300--399:很好,精確的時鐘源;
400--499:理想的時鐘源,如有可能就必須選擇它作為時鐘源;
2 read回撥函式
時鐘源本身不會產生中斷,要獲得時鐘源的當前計數,只能通過主動呼叫它的read回撥函式來獲得當前的計數值,注意這裡只能獲得計數值,也就是所謂的cycle,要獲得相應的時間,必須要藉助clocksource的mult和shift欄位進行轉換計算。
1.3 mult和shift欄位
因為從clocksource中讀到的值是一個cycle計數值,要轉換為時間,我們必須要知道驅動clocksource的時鐘頻率F,一個簡單的計算就可以完成:
t = cycle/F;
可是clocksource並沒有儲存時鐘的頻率F,因為使用上面的公式進行計算,需要使用浮點運算,這在核心中是不允許的,因此,核心使用了另外一個變通的辦法,根據時鐘的頻率和期望的精度,事先計算出兩個輔助常數mult和shift,然後使用以下公式進行cycle和t的轉換:
t = (cycle * mult) >> shift;只要我們保證:F = (1 << shift) / mult;核心內部使用64位進行該轉換計算:
xtime 是人們日常所使用的牆上時間
monotonic time 該時間自系統開機後就一直單調地增加,它不像xtime可以因使用者的調整時間而產生跳變,不過該時間不計算系統休眠的時間,也就是說,系統休眠時,monotoic時間不會遞增。核心用timekeeper結構來組織與時間相關的資料,它的定義如下
四, Struct timekeeper
14struct timekeeper {
15 /* Current clocksource used for timekeeping. */
16 struct clocksource *clock;
17 /* NTP adjusted clock multiplier */
18 u32 mult;
19 /* The shift value of the current clocksource. */
20 u32 shift;
21 /* Number of clock cycles in one NTP interval. */
22 cycle_t cycle_interval;
23 /* Last cycle value (also stored in clock->cycle_last) */
24 cycle_t cycle_last;
25 /* Number of clock shifted nano seconds in one NTP interval. */
26 u64 xtime_interval;
27 /* shifted nano seconds left over when rounding cycle_interval */
28 s64 xtime_remainder;
29 /* Raw nano seconds accumulated per NTP interval. */
30 u32 raw_interval;
31
32 /* Current CLOCK_REALTIME time in seconds */
33 u64 xtime_sec;
34 /* Clock shifted nano seconds */
35 u64 xtime_nsec;
36
37 /* Difference between accumulated time and NTP time in ntp
38 * shifted nano seconds. */
39 s64 ntp_error;
40 /* Shift conversion between clock shifted nano seconds and
41 * ntp shifted nano seconds. */
42 u32 ntp_error_shift;
43
44 /*
45 * wall_to_monotonic is what we need to add to xtime (or xtime corrected
46 * for sub jiffie times) to get to monotonic time. Monotonic is pegged
47 * at zero at system boot time, so wall_to_monotonic will be negative,
48 * however, we will ALWAYS keep the tv_nsec part positive so we can use
49 * the usual normalization.
50 *
51 * wall_to_monotonic is moved after resume from suspend for the
52 * monotonic time not to jump. We need to add total_sleep_time to
53 * wall_to_monotonic to get the real boot based time offset.
54 *
55 * - wall_to_monotonic is no longer the boot time, getboottime must be
56 * used instead.
57 */
58 struct timespec wall_to_monotonic;
59 /* Offset clock monotonic -> clock realtime */
60 ktime_t offs_real;
61 /* time spent in suspend */
62 struct timespec total_sleep_time;
63 /* Offset clock monotonic -> clock boottime */
64 ktime_t offs_boot;
65 /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
66 struct timespec raw_time;
67 /* The current UTC to TAI offset in seconds */
68 s32 tai_offset;
69 /* Offset clock monotonic -> clock tai */
70 ktime_t offs_tai;
71
72};
核心定義了一個變數wall_to_monotonic,記錄了牆上時間和monotonic時間之間的偏移量,當需要獲得monotonic時間時,把xtime和wall_to_monotonic相加即可,
Timekeeper的初始化,timekeeper的初始化由timekeeping_init完成,該函式在start_kernel的初始化序列中被呼叫,時間的更新:xtime一旦初始化完成後,timekeeper就開始獨立於RTC,利用自身關聯的clocksource進行時間的更新操作,根據核心的配置項的不同,更新時間的操作發生的頻度也不盡相同,如果沒有配置NO_HZ選項,通常每個tick的定時中斷週期,do_timer會被呼叫一次。