摘要:該文對(duì)Linux時(shí)鐘機(jī)制進(jìn)行研究,分析了影響時(shí)鐘慢的原因并設(shè)計(jì)了細(xì)粒度定時(shí)器,把系統(tǒng)時(shí)鐘的節(jié)拍的粒度減小,較大的提升了Linux的實(shí)時(shí)性能。
關(guān)鍵詞:Linux內(nèi)核;時(shí)鐘;定時(shí)器
中圖分類號(hào):TP274文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):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
由于標(biāo)準(zhǔn)Linux是一個(gè)分時(shí)操作系統(tǒng),因此在實(shí)時(shí)性方面存在許多問題,如何增強(qiáng)Linux操作系統(tǒng)的實(shí)時(shí)性能,使其滿足實(shí)時(shí)系統(tǒng)的需要,目前對(duì)Linux實(shí)時(shí)性的研究是國(guó)內(nèi)外學(xué)者研究的一個(gè)熱點(diǎn)。本文對(duì)Linux時(shí)鐘機(jī)制進(jìn)行研究,分析了影響時(shí)鐘慢的原因并設(shè)計(jì)了細(xì)粒度定時(shí)器,把系統(tǒng)時(shí)鐘的節(jié)拍的粒度減小,較大的提升了Linux的實(shí)時(shí)性能。
1 Linux內(nèi)核時(shí)鐘機(jī)制
與Linux2.4內(nèi)核相比,Linux2.6內(nèi)核將內(nèi)核時(shí)間頻率“Hz”,由原來的100提高到了1000,對(duì)于內(nèi)核的時(shí)鐘精度的提升是顯而易見的:更高的時(shí)鐘中斷解析度(resolution )可以提高時(shí)間驅(qū)動(dòng)時(shí)間的解析度(1ms);提高了時(shí)間驅(qū)動(dòng)事件的準(zhǔn)確度(accuracy)(事件的響應(yīng)的平均誤差降低10倍(0.5ms);內(nèi)核定時(shí)器能夠以更高的頻度和更高的準(zhǔn)確度運(yùn)行;依賴定時(shí)值執(zhí)行的系統(tǒng)調(diào)用,如po11Q和selectQ能夠以更高的精度運(yùn)行;對(duì)諸如資源消耗和系統(tǒng)運(yùn)行時(shí)間等的測(cè)量會(huì)有更精細(xì)的解析度;提高進(jìn)程搶占的準(zhǔn)確度,加快調(diào)度響應(yīng)的時(shí)間。
然而,即使是1ms的時(shí)鐘精度,對(duì)于高精度的實(shí)時(shí)應(yīng)用來說仍然是不夠的。大量的高實(shí)時(shí)性應(yīng)用中,時(shí)鐘精度往往要求達(dá)到微秒級(jí),否則大量實(shí)時(shí)進(jìn)程將無法得到即時(shí)響應(yīng)。一個(gè)簡(jiǎn)單而直接的方法是繼續(xù)提高內(nèi)核的時(shí)間頻率“Hz”,如提高到1000000,即可達(dá)到1us的時(shí)鐘精度。但是,這樣大幅度地提高時(shí)鐘精度的方法是以嚴(yán)重降低系統(tǒng)負(fù)載性能為代價(jià)的:時(shí)間頻率提高1000倍,意味著時(shí)鐘中斷頻率提高了1000倍,使中斷處理程序占用CPU時(shí)間提高了1000倍,從而減少了CPU處理其他工作的時(shí)間,更頻繁的擾亂了CPU的高速緩存,最終增加了系統(tǒng)的負(fù)載,嚴(yán)重降低了系統(tǒng)性能。
因此,需要考慮更加有效的改造方案,以便達(dá)到系統(tǒng)微秒級(jí)時(shí)鐘精度和調(diào)度負(fù)載增加的平衡。
2 細(xì)粒度定時(shí)器的實(shí)現(xiàn)原理
8253/8254定時(shí)器工作于單次觸發(fā)模式(One-shot Mode)下,將從CPU時(shí)間戳計(jì)數(shù)器TSC(Timestamp Counter)中獲得高精度的時(shí)間基值,然后根據(jù)實(shí)時(shí)任務(wù)的執(zhí)行時(shí)間設(shè)置定時(shí)器計(jì)數(shù)初始值寄存器,由于TSC存儲(chǔ)著系統(tǒng)啟動(dòng)以來CPU所經(jīng)歷的時(shí)鐘周期數(shù),所以它的精度可以達(dá)到納秒級(jí),輕松地滿足了實(shí)時(shí)應(yīng)用中的高時(shí)鐘精度要求。和周期模式不同,在這種模式下,程序員把預(yù)先計(jì)算好的數(shù)值送至鎖存器,然后啟動(dòng)定時(shí)器。硬件在自動(dòng)把鎖存器的內(nèi)容拷貝到計(jì)數(shù)器,當(dāng)計(jì)數(shù)器計(jì)數(shù)到0時(shí),產(chǎn)生一個(gè)中斷后,便停止工作,直到有軟件再次啟動(dòng)為止。通過對(duì)80x86中的三個(gè)定時(shí)器T/C0, T/C1和T/C2的設(shè)置,在系統(tǒng)初始化時(shí),向端口43H寫入控制字32H,即可控制時(shí)鐘以單次觸發(fā)模式工作。而下一次的中斷間隔需要在每次中斷產(chǎn)生后重新寫入鎖存器。在單次觸發(fā)模式下對(duì)時(shí)鐘中斷間隔進(jìn)行設(shè)置,即可實(shí)現(xiàn)時(shí)鐘以微秒級(jí)在任何需要的時(shí)候產(chǎn)生時(shí)鐘中斷。在任何時(shí)刻,時(shí)鐘的下一次中斷將由所有定時(shí)器到期時(shí)間中最早的一個(gè)來決定。一旦定時(shí)器到期,內(nèi)核便能立刻響應(yīng),因此內(nèi)核的響應(yīng)開銷只由中斷服務(wù)的時(shí)間所決定,大約只有幾個(gè)微秒。
此方法的基本原理:在系統(tǒng)中增加一個(gè)新的定時(shí)器鏈表,該鏈表中每一項(xiàng)的超時(shí)處理函數(shù)都在中斷環(huán)境中執(zhí)行,同時(shí)該鏈表的定時(shí)可以精確到微秒級(jí),這是通過x86處理器的時(shí)間戳寄存器將系統(tǒng)時(shí)鐘周期1毫秒進(jìn)一步細(xì)分來實(shí)現(xiàn)的。下面講述具體實(shí)現(xiàn)定時(shí)器鏈表類型:
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為還剩下多少個(gè)定時(shí)器周期該定時(shí)器到期,expires_sc為當(dāng)expires_jiffies為0時(shí),該定時(shí)還有多少時(shí)間到期,單位為tsc數(shù),即cpu時(shí)鐘周期數(shù),此項(xiàng)為實(shí)現(xiàn)微秒級(jí)定時(shí)器的關(guān)鍵所在。IRT_timers為定時(shí)器鏈表頭。當(dāng)時(shí)鐘中斷發(fā)生時(shí),系統(tǒng)將調(diào)用timer_interrupt0函數(shù),對(duì)timer_interrupt0函數(shù)修改如下:
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;
}……}
該函數(shù)首先判斷當(dāng)前時(shí)鐘中斷是處于什么狀態(tài),如果處于周期性模式(PERIOD_MOD),則查看在下個(gè)定時(shí)器周期里是否有定時(shí)器超時(shí)(PERIOD_MOD),則查看在下個(gè)定時(shí)器周期里是否有定時(shí)器超時(shí)(HRT_timers->expires_jiffies==0),如果有則將定時(shí)器設(shè)置為一次性觸發(fā)模式(ONE_SHOT_MOD),并利用定時(shí)器超時(shí)的tsc值,轉(zhuǎn)換成PIT counter重新設(shè)定定時(shí)器計(jì)數(shù)值,還要將該定時(shí)器從定時(shí)器鏈表中移到HRT_timers_should_process鏈表中,當(dāng)下次中斷時(shí),將調(diào)用該定時(shí)器的超時(shí)處理函數(shù)。因?yàn)樘幱谥芷谛阅J?,所以還要調(diào)用標(biāo)準(zhǔn)內(nèi)核的時(shí)鐘中斷處理函數(shù),以進(jìn)行標(biāo)準(zhǔn)內(nèi)核中與時(shí)鐘中斷相關(guān)的維護(hù)和更新工作。
如果時(shí)鐘處于一次性觸發(fā)模式(ONE_SHOT_MOD),說明有超時(shí)事件發(fā)生,則遍歷HRT_timers_should_process鏈表,并調(diào)用其中每個(gè)定時(shí)器的超時(shí)處理函數(shù),接著還會(huì)判斷該定時(shí)器周期是否還有將要超時(shí)的定時(shí)器,如果有則類似周期性模式利用定時(shí)器超時(shí)時(shí)的tsc值,轉(zhuǎn)換成PIT counter重新設(shè)定定時(shí)器計(jì)數(shù)值,還要將該定時(shí)器從定時(shí)器鏈表中移到HRT_timers_should_process鏈表中:如果該定時(shí)器周期沒有將要超時(shí)的定時(shí)器(HRT_timers->expires_jiffies!=0),則設(shè)置變量pit_mod為PERIOD_MOD以表明下次中斷為定時(shí)器周期中斷,但時(shí)定時(shí)器硬件此時(shí)仍處于一次性觸發(fā)模式,其計(jì)數(shù)值對(duì)應(yīng)的tsc個(gè)數(shù)為上次定時(shí)器周期中斷時(shí)的tsc值(HRT_period_tsc)加上每個(gè)定時(shí)器周期所對(duì)應(yīng)的tsc(TSC_PER_PERIOD)再減去當(dāng)前cpu的tsc值。
定時(shí)器硬件將在下個(gè)時(shí)鐘中斷時(shí)設(shè)置為周期性模式,上面即為新的時(shí)鐘中斷處理函數(shù)的大致處理過程。具體處理流程圖如圖1所示。對(duì)于定時(shí)器鏈表的組織還有2點(diǎn)需要說明一下:
1)定時(shí)器鏈表中,定時(shí)器超時(shí)時(shí)間是本定時(shí)器時(shí)間相對(duì)于前一定時(shí)器超時(shí)時(shí)間的差值,而不是定時(shí)器實(shí)際的超時(shí)時(shí)間,這樣的好處是不用在每個(gè)定時(shí)器中斷都要遍歷定時(shí)器鏈表對(duì)其進(jìn)行處理以更新超時(shí)時(shí)間,采用差值的方法后,只需在增加定時(shí)器時(shí)遍歷鏈表,進(jìn)行插入操作。
2)為了不產(chǎn)生2次中斷時(shí)間非常短的情況,本定時(shí)器設(shè)定一個(gè)中斷間隔時(shí)間門限值,該值可以通過系統(tǒng)調(diào)用進(jìn)行調(diào)整,因此在HRT_move_timer(HRT_timers_should_process,HRT_timers)中從HRT_timers移到HRT_ timers_ should_process中的超時(shí)定時(shí)器可能有多少。
3 系統(tǒng)測(cè)試
將通Linux為Red Hat 9,內(nèi)核版本2.6內(nèi)核為測(cè)試修改后的內(nèi)核的實(shí)時(shí)性能,測(cè)試按照兩種條件進(jìn)行;輕負(fù)載:Linux操作系統(tǒng)只是運(yùn)行了初始化進(jìn)程、shell進(jìn)程。重負(fù)載:在輕負(fù)載的情況下,拷貝一個(gè)大容量文件(100M)。
從表1,表2可以看出:標(biāo)準(zhǔn)的Linux在輕負(fù)載下的最長(zhǎng)調(diào)度誤差是2130us,在重負(fù)載下更是達(dá)到了25600us,而改進(jìn)的Linux內(nèi)核不論在輕負(fù)載下還是在重負(fù)是最長(zhǎng)調(diào)度延遲都在450us以下。所以改進(jìn)的Linux內(nèi)核的實(shí)時(shí)性能比原有的Linux內(nèi)核有較大的改善。
重負(fù)載的測(cè)試結(jié)果如表2。
4 結(jié)束語(yǔ)
隨著嵌入式系統(tǒng)越來越深入到人們的口常生活,廉價(jià)而高性能的Linux操作系統(tǒng)成了嵌入式開發(fā)人員的主要選擇之一。盡管Linux用于嵌入式系統(tǒng)中具有得天獨(dú)厚的優(yōu)勢(shì),它在實(shí)時(shí)性方面的缺陷也越來越明顯。因此,要將Linux用于嵌入式實(shí)時(shí)領(lǐng)域,需要對(duì)Linux內(nèi)核進(jìn)行實(shí)時(shí)化改造,使它更好地滿足實(shí)時(shí)應(yīng)用的要求。
參考文獻(xiàn):
[1] Michael Beck.Linux內(nèi)核編程指南[M].北京:清華大學(xué)出版社,2004.
[2] 趙慧斌,李小群.改善Linux核心可搶占性方法的研究與實(shí)現(xiàn)[J].計(jì)算機(jī)學(xué)報(bào),2004,27(2):240.
[3] 趙明富.Linux嵌入式系統(tǒng)實(shí)時(shí)性分析與實(shí)時(shí)化改進(jìn)[J].計(jì)算機(jī)應(yīng)用研究,2004,(4).
[4] 趙明富.嵌入式Linux操作系統(tǒng)的實(shí)時(shí)化研究[J].西南師范大學(xué)學(xué)報(bào):自然科學(xué)版,2003,28(3):86-90.