日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux下编写时钟代码,Linux时间子系统之一:clock source(时钟源)【转】(示例代码)...

發布時間:2024/10/14 linux 72 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux下编写时钟代码,Linux时间子系统之一:clock source(时钟源)【转】(示例代码)... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

clock source用于為linux內核提供一個時間基線,如果你用linux的date命令獲取當前時間,內核會讀取當前的clock source,轉換并返回合適的時間單位給用戶空間。在硬件層,它通常實現為一個由固定時鐘頻率驅動的計數器,計數器只能單調地增加,直到溢出為止。時鐘源是內核計時的基礎,系統啟動時,內核通過硬件RTC獲得當前時間,在這以后,在大多數情況下,內核通過選定的時鐘源更新實時時間信息(墻上時間),而不再讀取RTC的時間。本節的內核代碼樹基于V3.4.10。

/*****************************************************************************************************/

聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請注明出處,謝謝!

/*****************************************************************************************************/

1. ?struct clocksource結構

內核用一個clocksource結構對真實的時鐘源進行軟件抽象,現在我們從clock source的數據結構開始,它的定義如下:

struct?clocksource?{

/*

*?Hotpath?data,?fits?in?a?single?cache?line?when?the

*?clocksource?itself?is?cacheline?aligned.

*/

cycle_t?(*read)(struct?clocksource?*cs);

cycle_t?cycle_last;

cycle_t?mask;

u32?mult;

u32?shift;

u64?max_idle_ns;

u32?maxadj;

#ifdef?CONFIG_ARCH_CLOCKSOURCE_DATA

struct?arch_clocksource_data?archdata;

#endif

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:?*/

#ifdef?CONFIG_CLOCKSOURCE_WATCHDOG

/*?Watchdog?related?data,?used?by?the?framework?*/

struct?list_head?wd_list;

cycle_t?cs_last;

cycle_t?wd_last;

#endif

}?____cacheline_aligned;

我們只關注clocksource中的幾個重要的字段。

1.1 ?rating:時鐘源的精度

同一個設備下,可以有多個時鐘源,每個時鐘源的精度由驅動它的時鐘頻率決定,比如一個由10MHz時鐘驅動的時鐘源,他的精度就是100nS。clocksource結構中有一個rating字段,代表著該時鐘源的精度范圍,它的取值范圍如下:

1--99: 不適合于用作實際的時鐘源,只用于啟動過程或用于測試;

100--199:基本可用,可用作真實的時鐘源,但不推薦;

200--299:精度較好,可用作真實的時鐘源;

300--399:很好,精確的時鐘源;

400--499:理想的時鐘源,如有可能就必須選擇它作為時鐘源;

1.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位進行該轉換計算:

static?inline?s64?clocksource_cyc2ns(cycle_t?cycles,?u32?mult,?u32?shift)

{

return?((u64)?cycles?*?mult)?>>?shift;

}

從轉換精度考慮,mult的值是越大越好,但是為了計算過程不發生溢出,mult的值又不能取得過大。為此內核假設cycle計數值被轉換后的最大時間值:10分鐘(600秒),主要的考慮是CPU進入IDLE狀態后,時間信息不會被更新,只要在10分鐘內退出IDLE,clocksource的cycle計數值就可以被正確地轉換為相應的時間,然后系統的時間信息可以被正確地更新。當然最后的結果不一定是10分鐘,它由clocksource_max_deferment進行計算,并保存max_idle_ns字段中,tickless的代碼要考慮這個值,以防止在NO_HZ配置環境下,系統保持IDLE狀態的時間過長。在這樣,由10分鐘這個假設的時間值,我們可以推算出合適的mult和shift值。

2. ?clocksource的注冊和初始化

通常,clocksource要在初始化階段通過clocksource_register_hz函數通知內核它的工作時鐘的頻率,調用的過程如下:

由上圖可見,最終大部分工作會轉由__clocksource_register_scale完成,該函數首先完成對mult和shift值的計算,然后根據mult和shift值,最終通過clocksource_max_deferment獲得該clocksource可接受的最大IDLE時間,并記錄在clocksource的max_idle_ns字段中。clocksource_enqueue函數負責按clocksource的rating的大小,把該clocksource按順序掛在全局鏈表clocksource_list上,rating值越大,在鏈表上的位置越靠前。

每次新的clocksource注冊進來,都會觸發clocksource_select函數被調用,它按照rating值選擇最好的clocksource,并記錄在全局變量curr_clocksource中,然后通過timekeeping_notify函數通知timekeeping,當前clocksource已經變更,關于timekeeping,我將會在后續的博文中闡述。

3. ?clocksource watchdog

系統中可能同時會注冊對個clocksource,各個clocksource的精度和穩定性各不相同,為了篩選這些注冊的clocksource,內核啟用了一個定時器用于監控這些clocksource的性能,定時器的周期設為0.5秒:

#define?WATCHDOG_INTERVAL?(HZ?>>?1)

#define?WATCHDOG_THRESHOLD?(NSEC_PER_SEC?>>?4)

當有新的clocksource被注冊時,除了會掛在全局鏈表clocksource_list外,還會同時掛在一個watchdog鏈表上:watchdog_list。定時器周期性地(0.5秒)檢查watchdog_list上的clocksource,WATCHDOG_THRESHOLD的值定義為0.0625秒,如果在0.5秒內,clocksource的偏差大于這個值就表示這個clocksource是不穩定的,定時器的回調函數通過clocksource_watchdog_kthread線程標記該clocksource,并把它的rate修改為0,表示精度極差。

4. ?建立clocksource的簡要過程

在系統的啟動階段,內核注冊了一個基于jiffies的clocksource,代碼位于kernel/time/jiffies.c:

struct?clocksource?clocksource_jiffies?=?{

.name???????=?"jiffies",

.rating?????=?1,?/*?lowest?valid?rating*/

.read???????=?jiffies_read,

.mask???????=?0xffffffff,?/*32bits*/

.mult???????=?NSEC_PER_JIFFY?<

.shift??????=?JIFFIES_SHIFT,

};

......

static?int?__init?init_jiffies_clocksource(void)

{

return?clocksource_register(&clocksource_jiffies);

}

core_initcall(init_jiffies_clocksource);

它的精度只有1/HZ秒,rating值為1,如果平臺的代碼沒有提供定制的clocksource_default_clock函數,它將返回該clocksource:

struct?clocksource?*?__init?__weak?clocksource_default_clock(void)

{

return?&clocksource_jiffies;

}

然后,在初始化的后段,clocksource的代碼會把全局變量curr_clocksource設置為上述的clocksource:

static?int?__init?clocksource_done_booting(void)

{

......

curr_clocksource?=?clocksource_default_clock();

......

finished_booting?=?1;

......

clocksource_select();

......

return?0;

}

fs_initcall(clocksource_done_booting);

當然,如果平臺級的代碼在初始化時也會注冊真正的硬件clocksource,所以經過clocksource_select()函數后,curr_clocksource將會被設為最合適的clocksource。如果clocksource_select函數認為需要切換更好的時鐘源,它會通過timekeeping_notify通知timekeeping系統,使用新的clocksource進行時間計數和更新操作。

總結

以上是生活随笔為你收集整理的linux下编写时钟代码,Linux时间子系统之一:clock source(时钟源)【转】(示例代码)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。