突兀地說高精度定時器,覺得摸不著腦子,起碼初學者會沮喪,而且從字面上來理解,很簡單,定時器嘛,精度低點,之後,就沒有之後了。雖然差不多就是那麼回事,只是上面牽涉到了一些別的細節上的問題。
工欲善其事必先利其器,在開始講之前,我們先利一下器:
2相關的用到的幾個原始碼檔案以及其路徑如下:
Hrtimers.txt(linux-3.2.12documentationtimers)
Hrtimer.c(linux-3.2.12kernel)
Hrtimer.h(linux-3.2.12includelinux)
2單純的在高精度定時器模式下操作高精度定時器,整個操作框架如下:
初始化hrtimer_init,透過hetimer結構體設定相關數據,例如定時時長等->開啟定時器hrtimer_start->運列寬精度定時器hrtimer_run_queues->觸發中斷,調用中斷反彈函數,hrtimer_interrupt->移除高精度定時器remove_hrtimer.
讀者現今腦袋裡有一個框架,具體驅動細節下文將一一論述。
先概述一下,可能會有些拗口的東西在上面難理解,不要緊,旁邊會有相關的代碼和事例來解釋。
?高精度定時器依時間在一棵黑紅樹上排序。
?她們獨立於週期時鐘linux 應用定時器,採用脈寬時間戳而非jiffies的時間尺寸。
先把Linux程式碼上面的高精度定時器相關的文檔拿出來,瞧瞧他的介紹,之後我再解釋一下,文檔路徑:Hrtimers.txt(linux-3.2.12documentationtimers)
文件內容…說實話文檔有點長,但是Linux的文檔維護度不是很高,我從上面找了幾句話翻譯下來,之後解釋一下:
?Thispatchintroducesanewsubsystemforhigh-resolutionkerneltimers.這句話裡的patch這個詞組有點意思,他是說高精度定時器作為一個補丁包被安裝到系統裡的,在2.6.16之前是沒有這個概念的。
?第二點,英文太長就不貼了,是說為何要用高精度定時器,由於每位系統都存在定時器,其實精度不高,相比較而言就稱之為低精度定時器。說白了就是須要高精準度。
?第三點,高精度定時器還有個特徵,就是他的框架在編譯的時侯是在內核裡,而且假如沒有配置高精度定時器,這麼高精度定時器是根據普通的定時器來運行。
?最後一點,高精度定時器是採用黑紅樹演算法實現的,而普通的定時器是採用時間輪循演算法實現的.
?另外,文件中還解釋了很多諸如時鐘來源、資料結構、紅黑樹等等問題,這種問題在下邊分開論述。
一、相關的資料結構
高幀率定時器所涉及的資料結構,我們從這幾大方面來考慮:
關於來源:就是說這個時鐘是如何來的,在hrtimer.h中定義了一個結構體,程式碼如下:
structhrtimer_clock_base{
structhrtimer_cpu_base*cpu_base;
intindex;//用來區分時鐘的屬性(一共有兩種,下邊將會提到)
clockid_tclockid;//每位CPU所支援的時鐘的ID
structtimerqueue_headactive;//正在啟用的定時器的黑紅樹枝節點
ktime_tresolution;//時鐘的幀率,毫秒
ktime_t(*get_time)(void);//拿來恢復目前時脈
ktime_tsoftirq_time;//在軟中斷中運列寬精度定時器佇列的時間
ktime_toffset;//更改定時器時鐘的額偏斜量
};
關於前面的幾個元素,,有些東西解釋一下。
高幀率定時器可以基於兩種時鐘(時鐘基礎,clockbase):一種是單調時鐘(CLOCK_MONOTONIC),在系統啟動時linux是什麼系統,從0開始;另一種時脈(CLOCK_REALTIME)表示系統的實際時間。上文的結構體structhrtimer_clock_base中,index元素就是拿來分辨是CLOCK_MONOTONIC還是CLOCK_REALTIME時脈的。對於系統的每一個CPU都提供了一個包含這兩種時脈基礎的資料結構,每一個時侯總時脈基礎都有一個黑紅樹,來排序所有待決的高精度定時器linux 應用定時器 ,而每位CPU都提供兩個時鐘基礎(單調時鐘和實際時間),所有定時器都按過期時間在黑紅樹上排序,假如定時器早已到期但其處理程序反彈函數仍未執行,則從黑紅樹遷移到一個陣列中。在調整即時時脈的時侯,會導致儲存在CLOCK_REALTIME時脈基礎上的定時器的過期時間值與目前實際時間之間的誤差。 offset陣列有助於修正這些情況,他表示定時器必須要校準的偏斜量。因為這只是一種臨時效應,極少發生。
在認識時鐘來源之前linux軟體下載,我們或許還應當認識一個結構體structhrtimer,程式碼如下:
structhrtimer{
structtimerqueue_nodenode;//定時器佇列節點,同時也管理node.expires,高精度定時器的絕對失效時間在其內部的演算法這個時間和定時器基於的時鐘有關(就是上文說的那兩種時基).
ktime_t_softexpires;//絕對的最早的到期時間
enumhrtimer_restart(*)(structhrtimer*);//定時器到期反彈函數
structhrtimer_clock_base*base;//指向時基的表針(每位CPU,每位時鐘)
unsignedlongstate;//狀態訊息,用來看位值
#ifdefCONFIG_TIMER_STATS
intstart_pid;//定時器統計區域儲存的開始計時的任務的pid
void*start_site;//定時器儲存目前定時的開始值
charstart_comm[16];//定時器統計區域名稱開始計時的儲存過程
#endif
};
以上這個結構體,對於使用者來說,只需關心三點,第一是數組,這個是定時器失效後的反彈函數,第二是expires,這個表示到期時間,第三是最後一句話,高精度定時器結構體的使用必須經過hrtimer_init()函數的初始化,hrtimer_init()函數屬於應用插口,所以放到前面說。這兒還有一個問題,也是高精度定時器的核心的問題,就是黑紅樹在高精度定時器的應用問題,其實現今講這個有點早,而且先讓讀者心中有那麼個底,Linux的傳統定時器透過時間輪演算法實現(timer.c),但hrtimer透過黑紅樹演算法實現。在structhrtimer上面有一個node域,類型為structtimerqueue_node,這個域代表了hrtimer在黑紅樹中的位置,注意一下,我參考的源代碼是3.2.12版本,在2.6.X版本中這個域的格式為structrb_node。這兒先跟讀者打聲招呼,有那麼回事,等在將具體用法時,我們在談談是怎樣實現的。
兩個重要的結構體說完了,因為要相容多核心處理器,因而會涉及到每位CPU的時基,結構體structhrtimer_cpu_base就是拿來定義每位CPU的時鐘的,目前每位CPU只對應於單調時鐘和實時時鐘,結構體如下:
structhrtimer_cpu_base{//單一CPU時基結構體
raw_spinlock_tlock;//鎖定相關的時基和定時器,載子鎖定
unsignedlongactive_bases;//用活動的定時器標記基礎的位元組
#ifdefCONFIG_HIGH_RES_TIMERS
ktime_texpires_next;//即將到期的下一個時間的絕對時間
inthres_active;//高幀率模式的狀態,布林變數
inthang_detected;//最新的被發覺的掛起的高精度定時器中斷
unsignedlongnr_events;//高精度定時器中斷總量
unsignedlongnr_retries;//高精度定時器中斷重試總量
unsignedlongnr_hangs;//高精度定時器中斷掛起總量
ktime_tmax_hang_time;//在高精度定時器中斷觸發最長時間
#endif
structhrtimer_clock_baseclock_base[HRTIMER_MAX_CLOCK_BASES];//此CPU時基底表針
};
裡面的三個結構體應是最基礎的,定義了高精度定時器相關的功能和元素,而且每一個CPU都全套一個定義的結構體,之後初始化hrtimers。
基本的結構體講完了,下邊開始講API插口。
首先是配置並初始化hrtimers的API。在開始的時侯我們講structhrtimer的時侯,提及要使用structhrtimer要先初始化,函數申明程式碼如下:
voidhrtimer_init(structhrtimer*timer,clockid_tclock_id,
enumhrtimer_modemode)//給定時鐘初始化定時器
debug_init(timer,clock_id,mode);
__hrtimer_init(timer,clock_id,mode);
以上函數實現了一個高精度定時器的初始化,下邊是相關元素的解釋:
/**
*hrtimer_init–給定時鐘初始化定時器
*@timer:即將被初始化的定時器
*@clock_id:即將被使用的時鐘
*@mode:定時器模式abs/rel
*/
mode可以使用五個常數,如下:
enumhrtimer_mode{
HRTIMER_MODE_ABS=0x0,/*時間是絕對的*/
HRTIMER_MODE_REL=0x1,/*時間是相對的*/
HRTIMER_MODE_PINNED=0x02,/*定時器被綁定到CPU*/
HRTIMER_MODE_ABS_PINNED=0x02,
HRTIMER_MODE_REL_PINNED=0x03,
};
hrtimer_init()函數上面呼叫了__hrtimer_init()函數,下邊是函數的原型:
staticvoid__hrtimer_init(structhrtimer*timer,clockid_tclock_id,enumhrtimer_modemode)
structhrtimer_cpu_base*cpu_base;
intbase;
memsettimer,0,sizeof(structhrtimer));
cpu_base=&__raw_get_cpu_var(hrtimer_bases);
if(clock_id==CLOCK_REALTIME&&mode!=HRTIMER_MODE_ABS)
clock_id=CLOCK_MONOTONIC;
base=hrtimer_clockid_to_base(clock_id);
timer->base=&cpu_base->clock_base[base];
timerqueue_init(&timer->node);
#ifdefCONFIG_TIMER_STATS
timer->start_site=NULL;
timer->start_pid=-1;
memset(timer->start_comm,0,TASK_COMM_LEN);
#endif
La fonction__hrtimer_init() appelle la structure structhrtimer_cpu_base pour initialiser le CPU et utilise la fonction memset(). Le prototype est le suivant :
void*memset(void*s,intc,size_tn)
inti;
char*ss=s;
pour(i=0;i ss[i]=c; retours; Cette fonction efface le contenu de la mémoire et termine l'initialisation, memset(timer,0,sizeof(structhrtimer)). Veuillez noter ici, il s'agit toujours d'un problème mentionné ci-dessus. Le code source que j'ai utilisé est 3.2.12, et le code source fourni en 2.6.X n'a que deux constantes
以上是高精度定時器的相關介紹及操作框架解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!