国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Linux:時間的維護(hù)者:timekeeper

2019-11-09 18:28:14
字體:
供稿:網(wǎng)友

http://blog.csdn.net/droidphone/article/details/7989566

本系列文章的前兩節(jié)討論了用于計時的時鐘源:clocksource,以及內(nèi)核內(nèi)部時間的一些表示方法,但是對于真實的用戶來說,我們感知的是真實世界的真實時間,也就是所謂的墻上時間,clocksource只能提供一個按給定頻率不停遞增的周期計數(shù),如何把它和真實的墻上時間相關(guān)聯(lián)?本節(jié)的內(nèi)容正是要討論這一點(diǎn)。

時間的種類

內(nèi)核管理著多種時間,它們分別是:

RTC時間wall time:墻上時間monotonic timeraw monotonic timeboot time:總啟動時間

RTC時間 在PC中,RTC時間又叫CMOS時間,它通常由一個專門的計時硬件來實現(xiàn),軟件可以讀取該硬件來獲得年月日、時分秒等時間信息,而在嵌入式系統(tǒng)中,有使用專門的RTC芯片,也有直接把RTC集成到Soc芯片中,讀取Soc中的某個寄存器即可獲取當(dāng)前時間信息。一般來說,RTC是一種可持續(xù)計時的,也就是說,不管系統(tǒng)是否上電,RTC中的時間信息都不會丟失,計時會一直持續(xù)進(jìn)行,硬件上通常使用一個后備電池對RTC硬件進(jìn)行單獨(dú)的供電。因為RTC硬件的多樣性,開發(fā)者需要為每種RTC時鐘硬件提供相應(yīng)的驅(qū)動程序,內(nèi)核和用戶空間通過驅(qū)動程序訪問RTC硬件來獲取或設(shè)置時間信息。

xtime xtime和RTC時間一樣,都是人們?nèi)粘K褂玫膲ι蠒r間,只是RTC時間的精度通常比較低,大多數(shù)情況下只能達(dá)到毫秒級別的精度,如果是使用外部的RTC芯片,訪問速度也比較慢,為此,內(nèi)核維護(hù)了另外一個wall time時間:xtime,取決于用于對xtime計時的clocksource,它的精度甚至可以達(dá)到納秒級別,因為xtime實際上是一個內(nèi)存中的變量,它的訪問速度非常快,內(nèi)核大部分時間都是使用xtime來獲得當(dāng)前時間信息。xtime記錄的是自1970年1月1日24時到當(dāng)前時刻所經(jīng)歷的納秒數(shù)。

monotonic time 該時間自系統(tǒng)開機(jī)后就一直單調(diào)地增加,它不像xtime可以因用戶的調(diào)整時間而產(chǎn)生跳變,不過該時間不計算系統(tǒng)休眠的時間,也就是說,系統(tǒng)休眠時,monotoic時間不會遞增。

raw monotonic time 該時間與monotonic時間類似,也是單調(diào)遞增的時間,唯一的不同是:raw monotonic time“更純凈”,他不會受到NTP時間調(diào)整的影響,它代表著系統(tǒng)獨(dú)立時鐘硬件對時間的統(tǒng)計。 boot time 與monotonic時間相同,不過會累加上系統(tǒng)休眠的時間,它代表著系統(tǒng)上電后的總時間。

時間種類 精度(統(tǒng)計單位) 訪問速度 累計休眠時間 受NTP調(diào)整的影響
RTC Yes Yes
xtime Yes Yes
monotonic No Yes
raw monotonic No
boot time Yes

struct timekeeper

內(nèi)核用timekeeper結(jié)構(gòu)來組織與時間相關(guān)的數(shù)據(jù),它的定義如下:

struct timekeeper { struct clocksource *clock; /* Current clocksource used for timekeeping. */ u32 mult; /* NTP adjusted clock multiplier */ int shift; /* The shift value of the current clocksource. */ cycle_t cycle_interval; /* Number of clock cycles in one NTP interval. */ u64 xtime_interval; /* Number of clock shifted nano seconds in one NTP interval. */ s64 xtime_remainder; /* shifted nano seconds left over when rounding cycle_interval */ u32 raw_interval; /* Raw nano seconds accumulated per NTP interval. */ u64 xtime_nsec; /* Clock shifted nano seconds remainder not stored in xtime.tv_nsec. */ /* Difference between accumulated time and NTP time in ntp * shifted nano seconds. */ s64 ntp_error; /* Shift conversion between clock shifted nano seconds and * ntp shifted nano seconds. */ int ntp_error_shift; struct timespec xtime; /* The current time */ struct timespec wall_to_monotonic; struct timespec total_sleep_time; /* time spent in suspend */ struct timespec raw_time; /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */ ktime_t offs_real; /* Offset clock monotonic -> clock realtime */ ktime_t offs_boot; /* Offset clock monotonic -> clock boottime */ seqlock_t lock; /* Seqlock for all timekeeper values */ };

其中的xtime字段就是上面所說的墻上時間,它是一個timespec結(jié)構(gòu)的變量,它記錄了自1970年1月1日以來所經(jīng)過的時間,因為是timespec結(jié)構(gòu),所以它的精度可以達(dá)到納秒級,當(dāng)然那要取決于系統(tǒng)的硬件是否支持這一精度。 內(nèi)核除了用xtime表示墻上的真實時間外,還維護(hù)了另外一個時間:monotonic time,可以把它理解為自系統(tǒng)啟動以來所經(jīng)過的時間,該時間只能單調(diào)遞增,可以理解為xtime雖然正常情況下也是遞增的,但是畢竟用戶可以主動向前或向后調(diào)整墻上時間,從而修改xtime值。但是monotonic時間不可以往后退,系統(tǒng)啟動后只能不斷遞增。奇怪的是,內(nèi)核并沒有直接定義一個這樣的變量來記錄monotonic時間,而是定義了一個變量wall_to_monotonic,記錄了墻上時間和monotonic時間之間的偏移量,當(dāng)需要獲得monotonic時間時,把xtime和wall_to_monotonic相加即可,因為默認(rèn)啟動時monotonic時間為0,所以實際上wall_to_monotonic的值是一個負(fù)數(shù),它和xtime同一時間被初始化,請參考timekeeping_init函數(shù)。

計算monotonic時間要去除系統(tǒng)休眠期間花費(fèi)的時間,內(nèi)核用total_sleep_time記錄休眠的時間,每次休眠醒來后重新累加該時間,并調(diào)整wall_to_monotonic的值,使其在系統(tǒng)休眠醒來后,monotonic時間不會發(fā)生跳變。因為wall_to_monotonic值被調(diào)整。所以如果想獲取boot time,需要加入該變量的值:

void get_monotonic_boottime(struct timespec *ts) { ...... do { seq = read_seqbegin(&timekeeper.lock); *ts = timekeeper.xtime; tomono = timekeeper.wall_to_monotonic; <span style="color:#ff0000;">sleep = timekeeper.total_sleep_time;</span> nsecs = timekeeping_get_ns(); } while (read_seqretry(&timekeeper.lock, seq)); set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); }

raw_time字段用來表示真正的硬件時間,也就是上面所說的raw monotonic time,它不受時間調(diào)整的影響,monotonic時間雖然也不受settimeofday的影響,但會受到ntp調(diào)整的影響,但是raw_time不受ntp的影響,他真的就是開完機(jī)后就單調(diào)地遞增。xtime、monotonic-time和raw_time可以通過用戶空間的clock_gettime函數(shù)獲得,對應(yīng)的ID參數(shù)分別是 CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW。 clock字段則指向了目前timekeeper所使用的時鐘源,xtime,monotonic time和raw time都是基于該時鐘源進(jìn)行計時操作,當(dāng)有新的精度更高的時鐘源被注冊時,通過timekeeping_notify函數(shù),change_clocksource函數(shù)將會被調(diào)用,timekeeper.clock字段將會被更新,指向新的clocksource。

早期的內(nèi)核版本中,xtime、wall_to_monotonic、raw_time其實是定義為全局靜態(tài)變量,到我目前的版本(V3.4.10),這幾個變量被移入到了timekeeper結(jié)構(gòu)中,現(xiàn)在只需維護(hù)一個timekeeper全局靜態(tài)變量即可:

static struct timekeeper timekeeper;

timekeeper的初始化

timekeeper的初始化由timekeeping_init完成,該函數(shù)在start_kernel的初始化序列中被調(diào)用,timekeeping_init首先從RTC中獲取當(dāng)前時間:

void __init timekeeping_init(void) { struct clocksource *clock; unsigned long flags; struct timespec now, boot; read_persistent_clock(&now); read_boot_clock(&boot);

然后對鎖和ntp進(jìn)行必要的初始化:

seqlock_init(&timekeeper.lock); ntp_init();

接著獲取默認(rèn)的clocksource,如果平臺沒有重新實現(xiàn)clocksource_default_clock函數(shù),默認(rèn)的clocksource就是基于jiffies的clocksource_jiffies,然后通過timekeeper_setup_inernals內(nèi)部函數(shù)把timekeeper和clocksource進(jìn)行關(guān)聯(lián):

write_seqlock_irqsave(&timekeeper.lock, flags); clock = clocksource_default_clock(); if (clock->enable) clock->enable(clock); timekeeper_setup_internals(clock);

利用RTC的當(dāng)前時間,初始化xtime,raw_time,wall_to_monotonic等字段:

timekeeper.xtime.tv_sec = now.tv_sec; timekeeper.xtime.tv_nsec = now.tv_nsec; timekeeper.raw_time.tv_sec = 0; timekeeper.raw_time.tv_nsec = 0; if (boot.tv_sec == 0 && boot.tv_nsec == 0) { boot.tv_sec = timekeeper.xtime.tv_sec; boot.tv_nsec = timekeeper.xtime.tv_nsec; } set_normalized_timespec(&timekeeper.wall_to_monotonic, -boot.tv_sec, -boot.tv_nsec);

最后,初始化代表實時時間和monotonic時間之間偏移量的offs_real字段,total_sleep_time字段初始化為0:

update_rt_offset(); timekeeper.total_sleep_time.tv_sec = 0; timekeeper.total_sleep_time.tv_nsec = 0; write_sequnlock_irqrestore(&timekeeper.lock, flags);

xtime字段因為是保存在內(nèi)存中,系統(tǒng)掉電后無法保存時間信息,所以每次啟動時都要通過timekeeping_init從RTC中同步正確的時間信息。其中,read_persistent_clock和read_boot_clock是平臺級的函數(shù),分別用于獲取RTC硬件時間和啟動時的時間,不過值得注意到是,到目前為止(我的代碼樹基于3.4版本),ARM體系中,只有tegra和omap平臺實現(xiàn)了read_persistent_clock函數(shù)。如果平臺沒有實現(xiàn)該函數(shù),內(nèi)核提供了一個默認(rèn)的實現(xiàn):

void __attribute__((weak)) read_persistent_clock(struct timespec *ts) { ts->tv_sec = 0; ts->tv_nsec = 0; } void __attribute__((weak)) read_boot_clock(struct timespec *ts) { ts->tv_sec = 0; ts->tv_nsec = 0; }

那么,其他ARM平臺是如何初始化xtime的?答案就是CONFIG_RTC_HCTOSYS這個內(nèi)核配置項,打開該配置后,driver/rtc/hctosys.c將會編譯到系統(tǒng)中,由rtc_hctosys函數(shù)通過do_settimeofday在系統(tǒng)初始化時完成xtime變量的初始化:

static int __init rtc_hctosys(void) { ...... err = rtc_read_time(rtc, &tm); ...... rtc_tm_to_time(&tm, &tv.tv_sec); do_settimeofday(&tv); ...... return err; } late_initcall(rtc_hctosys);

時間的更新

xtime一旦初始化完成后,timekeeper就開始獨(dú)立于RTC,利用自身關(guān)聯(lián)的clocksource進(jìn)行時間的更新操作,根據(jù)內(nèi)核的配置項的不同,更新時間的操作發(fā)生的頻度也不盡相同,如果沒有配置NO_HZ選項,通常每個tick的定時中斷周期,do_timer會被調(diào)用一次,相反,如果配置了NO_HZ選項,可能會在好幾個tick后,do_timer才會被調(diào)用一次,當(dāng)然傳入的參數(shù)是本次更新離上一次更新時相隔了多少個tick周期,系統(tǒng)會保證在clocksource的max_idle_ns時間內(nèi)調(diào)用do_timer,以防止clocksource的溢出:

void do_timer(unsigned long ticks) { jiffies_64 += ticks; update_wall_time(); calc_global_load(ticks); }

在do_timer中,jiffies_64變量被相應(yīng)地累加,然后在update_wall_time中完成xtime等時間的更新操作,更新時間的核心操作就是讀取關(guān)聯(lián)clocksource的計數(shù)值,累加到xtime等字段中,其中還設(shè)計ntp時間的調(diào)整等代碼,詳細(xì)的代碼就不貼了。

獲取時間

timekeeper提供了一系列的接口用于獲取各種時間信息。

void getboottime(struct timespec *ts); 獲取系統(tǒng)啟動時刻的實時時間void get_monotonic_boottime(struct timespec *ts); 獲取系統(tǒng)啟動以來所經(jīng)過的時間,包含休眠時間ktime_t ktime_get_boottime(void); 獲取系統(tǒng)啟動以來所經(jīng)過的c時間,包含休眠時間,返回ktime類型ktime_t ktime_get(void); 獲取系統(tǒng)啟動以來所經(jīng)過的c時間,不包含休眠時間,返回ktime類型void ktime_get_ts(struct timespec *ts) ; 獲取系統(tǒng)啟動以來所經(jīng)過的c時間,不包含休眠時間,返回timespec結(jié)構(gòu)unsigned long get_seconds(void); 返回xtime中的秒計數(shù)值struct timespec current_kernel_time(void); 返回內(nèi)核最后一次更新的xtime時間,不累計最后一次更新至今clocksource的計數(shù)值void getnstimeofday(struct timespec *ts); 獲取當(dāng)前時間,返回timespec結(jié)構(gòu)void do_gettimeofday(struct timeval *tv); 獲取當(dāng)前時間,返回timeval結(jié)構(gòu)
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 玛多县| 禹城市| 南岸区| 武川县| 合江县| 拜城县| 东港市| 石渠县| 鄂温| 南陵县| 连云港市| 林口县| 四子王旗| 青海省| 三门峡市| 桓台县| 兴化市| 周宁县| 克拉玛依市| 南投市| 杭州市| 砀山县| 汝南县| 漯河市| 乐昌市| 广州市| 泰州市| 天津市| 雷州市| 饶阳县| 左贡县| 赤壁市| 巨野县| 德江县| 定西市| 瓦房店市| 高阳县| 方城县| 历史| 武川县| 石河子市|