摘要:該文對Linux時鐘機制進行研究,分析了影響時鐘慢的原因并設計了細粒度定時器,把系統時鐘的節拍的粒度減小,較大的提升了Linux的實時性能。
關鍵詞:Linux內核;時鐘;定時器
中圖分類號:TP274文獻標識碼:A文章編號:1009-3044(2009)36-10259-02
Linux 2.6 Design of Fine-Grained Timer
TANG Liang
(Yongzhou Vocational and Technical College, Yongzhou 425000, China)
Abstract: This paper Linux clock mechanism studies to analyze the reasons that affect the clock slowly and design of the fine-grained timers to beat the system clock granularity decreases, the larger Linux real-time performance improved.
Key words: Linux kernel; clock; timer
由于標準Linux是一個分時操作系統,因此在實時性方面存在許多問題,如何增強Linux操作系統的實時性能,使其滿足實時系統的需要,目前對Linux實時性的研究是國內外學者研究的一個熱點。本文對Linux時鐘機制進行研究,分析了影響時鐘慢的原因并設計了細粒度定時器,把系統時鐘的節拍的粒度減小,較大的提升了Linux的實時性能。
1 Linux內核時鐘機制
與Linux2.4內核相比,Linux2.6內核將內核時間頻率“Hz”,由原來的100提高到了1000,對于內核的時鐘精度的提升是顯而易見的:更高的時鐘中斷解析度(resolution )可以提高時間驅動時間的解析度(1ms);提高了時間驅動事件的準確度(accuracy)(事件的響應的平均誤差降低10倍(0.5ms);內核定時器能夠以更高的頻度和更高的準確度運行;依賴定時值執行的系統調用,如po11Q和selectQ能夠以更高的精度運行;對諸如資源消耗和系統運行時間等的測量會有更精細的解析度;提高進程搶占的準確度,加快調度響應的時間。
然而,即使是1ms的時鐘精度,對于高精度的實時應用來說仍然是不夠的。大量的高實時性應用中,時鐘精度往往要求達到微秒級,否則大量實時進程將無法得到即時響應。一個簡單而直接的方法是繼續提高內核的時間頻率“Hz”,如提高到1000000,即可達到1us的時鐘精度。但是,這樣大幅度地提高時鐘精度的方法是以嚴重降低系統負載性能為代價的:時間頻率提高1000倍,意味著時鐘中斷頻率提高了1000倍,使中斷處理程序占用CPU時間提高了1000倍,從而減少了CPU處理其他工作的時間,更頻繁的擾亂了CPU的高速緩存,最終增加了系統的負載,嚴重降低了系統性能。
因此,需要考慮更加有效的改造方案,以便達到系統微秒級時鐘精度和調度負載增加的平衡。
2 細粒度定時器的實現原理
8253/8254定時器工作于單次觸發模式(One-shot Mode)下,將從CPU時間戳計數器TSC(Timestamp Counter)中獲得高精度的時間基值,然后根據實時任務的執行時間設置定時器計數初始值寄存器,由于TSC存儲著系統啟動以來CPU所經歷的時鐘周期數,所以它的精度可以達到納秒級,輕松地滿足了實時應用中的高時鐘精度要求。和周期模式不同,在這種模式下,程序員把預先計算好的數值送至鎖存器,然后啟動定時器。硬件在自動把鎖存器的內容拷貝到計數器,當計數器計數到0時,產生一個中斷后,便停止工作,直到有軟件再次啟動為止。通過對80x86中的三個定時器T/C0, T/C1和T/C2的設置,在系統初始化時,向端口43H寫入控制字32H,即可控制時鐘以單次觸發模式工作。而下一次的中斷間隔需要在每次中斷產生后重新寫入鎖存器。在單次觸發模式下對時鐘中斷間隔進行設置,即可實現時鐘以微秒級在任何需要的時候產生時鐘中斷。在任何時刻,時鐘的下一次中斷將由所有定時器到期時間中最早的一個來決定。一旦定時器到期,內核便能立刻響應,因此內核的響應開銷只由中斷服務的時間所決定,大約只有幾個微秒。
此方法的基本原理:在系統中增加一個新的定時器鏈表,該鏈表中每一項的超時處理函數都在中斷環境中執行,同時該鏈表的定時可以精確到微秒級,這是通過x86處理器的時間戳寄存器將系統時鐘周期1毫秒進一步細分來實現的。下面講述具體實現定時器鏈表類型:
struct HRT_timer_list{
struct HRT_timer_list *next;
unsigned long expires_jiffies;
unsigned long expires_sc;
void (*function)(unsigned long);
unsigned long data;
}*HRT_timers;
expires_jiffies為還剩下多少個定時器周期該定時器到期,expires_sc為當expires_jiffies為0時,該定時還有多少時間到期,單位為tsc數,即cpu時鐘周期數,此項為實現微秒級定時器的關鍵所在。IRT_timers為定時器鏈表頭。當時鐘中斷發生時,系統將調用timer_interrupt0函數,對timer_interrupt0函數修改如下:
timer_interrupt()
{……
If(pit_mod==PERIOD_MOD){
HRT_period_tsc=read_tsc0;
If(should_set_period_mod==){
HRT_set_pit_mod(PERIOD_MOD, PERIOD_COUNTER);
Should_set_period_mod==0;}
If(HRT_timers->expires_jiffies==0)
Counter=HRT_tsc_to_pitcounter(HRT_timers-)expires_tsc);
HRT_set_pit_mod(ONE_SHOT_MOD, counter);
Pit_mod=ONE_SHOT_MOD;
HRT_move_time(HRT_timers_should_process, HRT_timers);}
Do_old_timer_interrupt0;
}else{
Should_set_period_mod=1;
Counter=HRT_tsc_to_pitcounter(TSCs_PER_PERIOD+HRT_period_tsc-read_tscO);
HRT_set_pit_mod(ONE_SHOT_MOD,counter);
Pit_mod=PERIOD_MOD;
}……}
該函數首先判斷當前時鐘中斷是處于什么狀態,如果處于周期性模式(PERIOD_MOD),則查看在下個定時器周期里是否有定時器超時(PERIOD_MOD),則查看在下個定時器周期里是否有定時器超時(HRT_timers->expires_jiffies==0),如果有則將定時器設置為一次性觸發模式(ONE_SHOT_MOD),并利用定時器超時的tsc值,轉換成PIT counter重新設定定時器計數值,還要將該定時器從定時器鏈表中移到HRT_timers_should_process鏈表中,當下次中斷時,將調用該定時器的超時處理函數。因為處于周期性模式,所以還要調用標準內核的時鐘中斷處理函數,以進行標準內核中與時鐘中斷相關的維護和更新工作。
如果時鐘處于一次性觸發模式(ONE_SHOT_MOD),說明有超時事件發生,則遍歷HRT_timers_should_process鏈表,并調用其中每個定時器的超時處理函數,接著還會判斷該定時器周期是否還有將要超時的定時器,如果有則類似周期性模式利用定時器超時時的tsc值,轉換成PIT counter重新設定定時器計數值,還要將該定時器從定時器鏈表中移到HRT_timers_should_process鏈表中:如果該定時器周期沒有將要超時的定時器(HRT_timers->expires_jiffies!=0),則設置變量pit_mod為PERIOD_MOD以表明下次中斷為定時器周期中斷,但時定時器硬件此時仍處于一次性觸發模式,其計數值對應的tsc個數為上次定時器周期中斷時的tsc值(HRT_period_tsc)加上每個定時器周期所對應的tsc(TSC_PER_PERIOD)再減去當前cpu的tsc值。
定時器硬件將在下個時鐘中斷時設置為周期性模式,上面即為新的時鐘中斷處理函數的大致處理過程。具體處理流程圖如圖1所示。對于定時器鏈表的組織還有2點需要說明一下:
1)定時器鏈表中,定時器超時時間是本定時器時間相對于前一定時器超時時間的差值,而不是定時器實際的超時時間,這樣的好處是不用在每個定時器中斷都要遍歷定時器鏈表對其進行處理以更新超時時間,采用差值的方法后,只需在增加定時器時遍歷鏈表,進行插入操作。
2)為了不產生2次中斷時間非常短的情況,本定時器設定一個中斷間隔時間門限值,該值可以通過系統調用進行調整,因此在HRT_move_timer(HRT_timers_should_process,HRT_timers)中從HRT_timers移到HRT_ timers_ should_process中的超時定時器可能有多少。
3 系統測試
將通Linux為Red Hat 9,內核版本2.6內核為測試修改后的內核的實時性能,測試按照兩種條件進行;輕負載:Linux操作系統只是運行了初始化進程、shell進程。重負載:在輕負載的情況下,拷貝一個大容量文件(100M)。
從表1,表2可以看出:標準的Linux在輕負載下的最長調度誤差是2130us,在重負載下更是達到了25600us,而改進的Linux內核不論在輕負載下還是在重負是最長調度延遲都在450us以下。所以改進的Linux內核的實時性能比原有的Linux內核有較大的改善。
重負載的測試結果如表2。
4 結束語
隨著嵌入式系統越來越深入到人們的口常生活,廉價而高性能的Linux操作系統成了嵌入式開發人員的主要選擇之一。盡管Linux用于嵌入式系統中具有得天獨厚的優勢,它在實時性方面的缺陷也越來越明顯。因此,要將Linux用于嵌入式實時領域,需要對Linux內核進行實時化改造,使它更好地滿足實時應用的要求。
參考文獻:
[1] Michael Beck.Linux內核編程指南[M].北京:清華大學出版社,2004.
[2] 趙慧斌,李小群.改善Linux核心可搶占性方法的研究與實現[J].計算機學報,2004,27(2):240.
[3] 趙明富.Linux嵌入式系統實時性分析與實時化改進[J].計算機應用研究,2004,(4).
[4] 趙明富.嵌入式Linux操作系統的實時化研究[J].西南師范大學學報:自然科學版,2003,28(3):86-90.