應用中的嵌入式Linux實(shí)時(shí)優(yōu)化技巧
它的開(kāi)發(fā)通常受到很多客觀(guān)條件的限制,諸如較弱的CPU處理能力、較小的內存空間、較少的可供選擇的外設、有限的電源供應等。每個(gè)嵌入式系統的開(kāi)發(fā)無(wú)不是精打細算,以求用有限的資源發(fā)揮最大的功效。在各種嵌入式系統上運行的操作系統中,嵌入式Linux以其免費、高可靠性、廣泛的硬件支持以及開(kāi)放源碼等眾多特性正在獲得越來(lái)越多的關(guān)注。其源碼開(kāi)放特性使得開(kāi)發(fā)者可以針對特定的嵌入式系統對Linux內核進(jìn)行修改,以滿(mǎn)足開(kāi)發(fā)要求,達到系統最優(yōu)化的目的。嵌入式Linux應用中的一大問(wèn)題是Linux的實(shí)時(shí)性問(wèn)題。實(shí)時(shí)系統必須在限定時(shí)間內對外部事件作出正確響應,重點(diǎn)放在對滿(mǎn)足突發(fā)性、暫時(shí)性的處理需求上。而Linux作為傳統的分時(shí)操作系統,關(guān)注更多的是系統的整體數據吞吐量。如何提高Linux的實(shí)時(shí)性能是擺在廣大嵌入式系統級開(kāi)發(fā)人員面前的挑戰。
1 相關(guān)研究
目前市面上有各種Linux發(fā)行版,但嚴格來(lái)說(shuō),Linux指的是Linus Torvalds維護的(及通過(guò)主要和鏡像網(wǎng)站發(fā)布的)內核。建立嵌入式系統不需要特別的內核,一個(gè)嵌入式Linux系統只是代表一個(gè)基于Linux內核的嵌入式系統,本文后面提及的Linux均指Linux內核。目前已有很多改善Linux實(shí)時(shí)性能的工作在進(jìn)行中。最新的2.6版Linux已經(jīng)實(shí)現了可搶占式的內核任務(wù)調度,但是不確定的中斷延時(shí)問(wèn)題沒(méi)有得到解決。即2.6版的Linux高優(yōu)先級內核空間進(jìn)程雖然能像在用戶(hù)空間里那樣搶占低優(yōu)先級進(jìn)程的系統資源,但是從中斷發(fā)出開(kāi)始到中斷服務(wù)程序的第1條指令開(kāi)始執行的時(shí)間是不確定的。
除了Linux開(kāi)發(fā)者的改進(jìn)工作之外,還有一些組織、公司為提高Linux的實(shí)時(shí)性做了大量工作。其中有代表性的是Fsm Labs公司的RT-Linux、Monta Vista公司的MontaVista Linux、由Paolo Mantegazza等人維護的RTAI(Realtime Application InteRFace)項目。這些項目采用的方法可歸納為兩類(lèi):
(1)直接修改Linux內核。MontaVista Linux采用的就是這種方法。它將Linux修改成稱(chēng)為Relatively Fully Preemptable Kernel的可搶占式內核,并使現了實(shí)時(shí)調度機制和算法,增加了一個(gè)細粒度定時(shí)器,這樣就將Linux修改成為一個(gè)軟實(shí)時(shí)內核。
(2)“雙內核”方式。RTAI項目和RT-Linux采用了這種方法。這種方法將傳統Linux“架空”,作為新增加的小型實(shí)時(shí)內核的一個(gè)優(yōu)先級最低的任務(wù)執行,而實(shí)時(shí)任務(wù)則作為優(yōu)先級最高的任務(wù)。即在實(shí)時(shí)任務(wù)存在的情況下運行實(shí)施任務(wù),否則才運行Linux本身的任務(wù)。
MontaVista和RT-Linux的局限性在于它是一個(gè)商用軟件,不遵循GNU 的源代碼開(kāi)放原則。若要在系統中使用這種Linux,則需要支付一筆頗為可觀(guān)的授權費用,這就違背了使用Linux的初衷—— 開(kāi)源、免費,能夠發(fā)展自己的知識產(chǎn)權。
RTAI為了實(shí)時(shí)性能舍棄了Linux固有的很多優(yōu)點(diǎn):對大量硬件的廣泛支持,優(yōu)秀的穩定性、可靠性。開(kāi)發(fā)者一方面要針對RTAI自定義的一個(gè)硬件抽象層RTHAL(Real Time Hardware Abstraction Layer)重新編寫(xiě)驅動(dòng)程序,而且龐大的Linux開(kāi)發(fā)社區的成果也無(wú)法方便地應用到實(shí)時(shí)核心中。
2 影響Linux實(shí)時(shí)性的因素
2.1 任務(wù)切換及其延時(shí)
任務(wù)切換延時(shí)是Linux從一個(gè)進(jìn)程切換到另一個(gè)進(jìn)程所需的時(shí)間,即高優(yōu)先級進(jìn)程從發(fā)出CPU 資源申請到進(jìn)程的第1條指令開(kāi)始執行的間隔。在實(shí)時(shí)系統中,任務(wù)切換延時(shí)必須越短越好。如之前所提到的,Linux 2.6.X已經(jīng)實(shí)現了可搶占式內核,高優(yōu)先級內核空間進(jìn)程可以像在用戶(hù)空間中那樣讓CPU在任何時(shí)候停止低優(yōu)先級進(jìn)程轉而執行自己。但是有2種例外情況:
(1)進(jìn)程在臨界區(Critical Section)中執行的時(shí)候不能被其他進(jìn)程搶占;
(2)中斷服務(wù)程序(Interrupt Service Routine)不能被其他進(jìn)程搶占。
2.2 基于優(yōu)先級的調度算法
在Linux 2.6中,采用了O(1)調度算法。它是一個(gè)基于優(yōu)先級的搶先式調度器,為每一個(gè)進(jìn)程分配一個(gè)唯一的優(yōu)先級,調度器保證在所有等待運行的任務(wù)中,首先被執行的總是高優(yōu)先級的任務(wù),為此高優(yōu)先級的任務(wù)能夠搶占低優(yōu)先級的任務(wù)。
這個(gè)調度器開(kāi)銷(xiāo)恒定,與當前系統開(kāi)銷(xiāo)無(wú)關(guān),能夠改善系統實(shí)時(shí)性能。但是調度系統沒(méi)有提供除CPU以外的其他資源剝奪運行,實(shí)時(shí)性能沒(méi)有得到根本改觀(guān)。如果兩個(gè)任務(wù)需要使用同一個(gè)資源(如高速緩存),高優(yōu)先級的任務(wù)已準備就緒,而此刻低優(yōu)先級的任務(wù)正在用這個(gè)資源,高優(yōu)先級的任務(wù)就必須等待,直到低優(yōu)先級任務(wù)結束釋放了該資源后才能被執行,這被稱(chēng)為優(yōu)先級倒置。
2.3 中斷延時(shí)、中斷服務(wù)程序
中斷延時(shí)指的是從外設發(fā)出中斷信號開(kāi)始到ISR的第1條指令開(kāi)始執行的時(shí)間間隔。由外部中斷引起的實(shí)時(shí)任務(wù)需求是實(shí)時(shí)系統處理量的主要組成部分,足夠快地中斷響應和迅速地中斷服務(wù)程序處理是衡量實(shí)時(shí)系統的重要性能指標。不同的ISR執行時(shí)間是不一樣的,即使是相同的ISR也可能因為有多個(gè)出口而有不同的執行時(shí)間。而ISR執行時(shí)外部中斷是被禁用的,造成這樣一種情況,即使Linux的中斷延時(shí)非常小,如果在一個(gè)ISR執行時(shí)某個(gè)外設也產(chǎn)生了一個(gè)中斷信號,因為正在執行的ISR運行時(shí)間的不確定性和不可搶占性,也會(huì )產(chǎn)生Linux的中斷延時(shí)的不可預測性。
3 系統實(shí)時(shí)性能的提高
3.1 任務(wù)切換機倒的建立
在2.1節中提到進(jìn)程在臨界區中執行的時(shí)候不能被搶占的問(wèn)題,為了不影響系統穩定性、減少調試和測試的時(shí)間,我們不打算對此進(jìn)行修改,而引入一個(gè)機制保證實(shí)時(shí)任務(wù)能夠得到優(yōu)先執行。即在實(shí)時(shí)系統中,只有當進(jìn)程的臨界區能在下一個(gè)實(shí)時(shí)任務(wù)開(kāi)始之前結束才被允許進(jìn)入。
如何判斷下一個(gè)實(shí)時(shí)任務(wù)中斷信號的產(chǎn)生時(shí)間,一般來(lái)說(shuō)中斷信號是為了那些開(kāi)始時(shí)間不可預測的任務(wù)而設定的,它的產(chǎn)生是完全隨機的。為了使中斷信號的時(shí)間能夠被預測,將中斷信號的產(chǎn)生與時(shí)鐘中斷掛鉤:中斷信號只能與時(shí)鐘中斷同時(shí)產(chǎn)生。時(shí)鐘中斷由系統計時(shí)硬件以周期性間隔產(chǎn)生。這個(gè)間隔由內核根據Hz值設定。Hz是一個(gè)與體系結構有關(guān)的常數,在文件中定義。當前的Linux為大多數平臺定義的Hz值是100,亦即時(shí)鐘中斷周期是10ms。顯然這是達不到實(shí)時(shí)系統定時(shí)精度要求的。提高Hz值能夠帶來(lái)系統性能提升,但卻是以增加系統開(kāi)銷(xiāo)為代價(jià)。這就必須仔細權衡實(shí)時(shí)性要求和系統開(kāi)銷(xiāo)的平衡。一種方法是通過(guò)大量的測試確定的實(shí)時(shí)任務(wù)中斷請求發(fā)生的時(shí)間間隔和進(jìn)程在臨界區中的執行時(shí)間,取一個(gè)稍大于大多數實(shí)時(shí)任務(wù)中斷間隔和臨界區執行時(shí)間的數值。
Linux提供了一些機制讓我們得以計算函數的執行時(shí)間,gettimefoday()函數是其中之一。函數的原型及需要使用的一個(gè)數據結構如下:

其中,gettimeofday()將當前時(shí)間保存在tv結構中,tz一般不需要用到,可用NULL代替。使用示例如下:

如此即可得出進(jìn)程在臨界區function_in_critical_section()所耗費的時(shí)間,以供參考。將Hz值設定在2000,此時(shí)系統時(shí)鐘中斷周期為0.5ms,精度提高了20倍。
如圖1、圖2所示,當進(jìn)程進(jìn)入臨界區之前,它比較自身的平均執行時(shí)間T(NP)和T(REMAIN)的值,當T(NP)≤T(REMAIN)的時(shí)候,進(jìn)程才被允許進(jìn)入臨界區,否則進(jìn)程進(jìn)入工作隊列等待下一次判斷。

本文嘗試用數學(xué)方法來(lái)分析采用這種機制對實(shí)時(shí)性能的提高。首先給出一個(gè)定義:當預定在時(shí)刻t時(shí)執行的實(shí)時(shí)任務(wù)推遲到時(shí)刻t'時(shí)才執行,則t'-t稱(chēng)作系統延遲,用Lat(OS)表示。在普通Linux中,Lat(OS)如下:
Lat(OS)=T(NP)+ T(SHED)
設任意時(shí)刻 ,T(NP)≤T(REMAIN)的機率為ρ,則普通Linux中的平均Lat(OS)為
AvLat(OS)=ρ[T(NP)+ T(SHED)] +(1-ρ)[T(NP)+ 2T(SHED)]
引入前述機制后,由于總是優(yōu)先保證實(shí)時(shí)任務(wù)的執行,Lat(RT-OS)固定式為:
Lat(RT-OS)=T(SHED)
采用該機制前后系統廷遲的變化為
δ=AvLat(NOR-OS)-Lat(RT-OS)=T(NP)+(2-ρ)T(SHED)
在一個(gè)特定系統里,ρ是固定的,而在Linux 2.6中,采用O(1)算法后T(SHED)也是固定的,由前式可得出結論:在臨界區的進(jìn)程執行時(shí)間長(cháng)的系統中,引入該機制前后平均系統廷遲下降的越大,系統實(shí)時(shí)性能的改善越明顯。
3.2 優(yōu)先級量頂
試描述一個(gè)如下場(chǎng)景:低優(yōu)先級的任務(wù)L和高優(yōu)先級H任務(wù)需要占用同一共享資源,低優(yōu)先級任務(wù)開(kāi)始后不久,高優(yōu)先級任務(wù)也準備就緒,發(fā)現所需共享資源被占用后,任務(wù)H被掛起,等待任務(wù)L結束釋放該資源。此時(shí)一個(gè)不需要該資源的中優(yōu)先級任務(wù)M 出現,調度器依據優(yōu)先原則轉而執行任務(wù)M。這就進(jìn)一步廷長(cháng)了任務(wù)H的等待時(shí)間,如圖3所示。更加惡劣的情況是,如果出現了更多的類(lèi)似任務(wù)M0,M1,M2,...,將有可能使任務(wù)H錯過(guò)臨界期限(Critical Deadline),而導致系統崩潰。
在一個(gè)不太復雜的實(shí)時(shí)系統中,可采用優(yōu)先級置頂的方法解決這一問(wèn)題。該方案對每一個(gè)可能被共享的資源分配一個(gè)優(yōu)先級,該優(yōu)先級為有可能使用這個(gè)資源的最高優(yōu)先級的進(jìn)程的優(yōu)先級(如下偽代碼中的RESOURCE_X_PRIO)。由調度器將優(yōu)先級傳給使用該資源的進(jìn)程,進(jìn)程結束后其自身的優(yōu)先級(如下偽代碼中的TASK_A_PRIO)才恢復正常。這樣就避免了上面場(chǎng)景中任務(wù)L被任務(wù)M搶占,而導致任務(wù)H始終處于掛起狀態(tài)。優(yōu)先級置頂的示例代碼如下:

3.3 內核線(xiàn)程
中斷服務(wù)程序(ISR)是不能被搶占的。一旦CPU 開(kāi)始執行ISR,除非程序結束,否則不可能轉而執行其他的任務(wù)。Linux用自旋鎖(Spinlock)來(lái)實(shí)現ISR對CPU的獨占。采用了自旋鎖的ISR是不能進(jìn)入休眠的,而且此時(shí)系統的中斷也被完全禁止。內核線(xiàn)程是由內核創(chuàng )建和撤銷(xiāo)的,用來(lái)執行一個(gè)指定的函數。內核線(xiàn)程具有自己的內核堆棧,能夠被單獨調用。我們用內核線(xiàn)程代替ISR,并且用互斥量(Mutex)替換自旋鎖。內核線(xiàn)程能夠進(jìn)入休眠,而且執行時(shí)是不禁用外部中斷的。系統接到中斷信號后,喚醒相應的內核線(xiàn)程,內核線(xiàn)程代替原來(lái)的ISR執行完任務(wù)后繼續進(jìn)入休眠狀態(tài)。這樣中斷廷時(shí)就是可預測的,并且占用時(shí)間也很少。
根據LynuxWorks公司的測試數據,在Pentium III 1GHz的PC上,Linux 2.4內核的平均任務(wù)響應時(shí)間為1133us,平均中斷響應時(shí)間為252us;而Linux 2.6內核的平均響應時(shí)間為132us,平均中斷響應時(shí)間僅為14us,比Linux 2.4內核提高了一個(gè)數量級。在此基礎上,采用這種方法能夠針對具體的系統進(jìn)一步加快特定中斷的響應時(shí)間,提高應用系統的實(shí)時(shí)性能。
4 總結與展望
本文以L(fǎng)inux 2.6為基礎探討了提高Linux實(shí)時(shí)性的方法。引入了在實(shí)時(shí)系統中,只有當進(jìn)入臨界區的進(jìn)程能在下一個(gè)實(shí)時(shí)任務(wù)開(kāi)始之前結束時(shí)才被允許執行的機制,保證實(shí)時(shí)任務(wù)總是優(yōu)先得到執行;采用了優(yōu)先級置頂的方法避免了出現優(yōu)先級倒置的情況;用內核線(xiàn)程代替中斷服務(wù)程序,改變了了一般中斷服務(wù)程序執行中不能進(jìn)入休眠狀態(tài)的情況,并且執行時(shí)不禁用外部中斷,使系統的中斷廷時(shí)變得短小和可預測。本文所述方法的缺點(diǎn)在于,提高系統時(shí)鐘中斷頻率帶會(huì )增大系統開(kāi)銷(xiāo)問(wèn)題。為了在實(shí)時(shí)性能提升和系統開(kāi)銷(xiāo)增大之間找到一個(gè)平衡點(diǎn),開(kāi)發(fā)者不得不對具體系統做大量測試,具體問(wèn)題具體分析,使得該方法在適用性上打了折扣。Linux因其免費、性能強大、工具眾多的特點(diǎn),必將在嵌入式系統領(lǐng)域得到大量的應用。我們應該及時(shí)跟蹤國內外Linux發(fā)展動(dòng)態(tài),同時(shí)積累在此領(lǐng)域的開(kāi)發(fā)經(jīng)驗,走出自己的路來(lái)。
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)
評論