改善Linux內(nèi)核實時性方法的研究與實現(xiàn)
由于Linux具有功能強大、源代碼開放、支持多種硬件平臺、模塊化的設(shè)計方案以及豐富的開發(fā)工具支持等特點,在實際系統(tǒng)中,得到了廣泛的應(yīng)用。但由于其最初的設(shè)計目標為通用分時操作系統(tǒng),對于實時系統(tǒng)而言,Linux仍然存在核心不可搶占、關(guān)中斷、時鐘粒度
粗糙等缺陷。為了使其應(yīng)用于嵌入式系統(tǒng),實時控制等領(lǐng)域,越來越多的廠家和研究機構(gòu)熱衷于改善其實時性,構(gòu)建基于Linux的實時操作系統(tǒng)。
在Linux 2.4和以前的版本,內(nèi)核是不可搶占的,也就是說,如果當前任務(wù)運行在內(nèi)核態(tài),即使當前有更緊急的任務(wù)需要運行,當前任務(wù)也不能被搶占。因此那個緊急任務(wù)必須等到當前任務(wù)執(zhí)行完內(nèi)核態(tài)的操作返回用戶態(tài)后或當前任務(wù)因需要等待某些條件滿足而主動讓出CPU才能被考慮執(zhí)行,這很明顯嚴重影響搶占延遲。在Linux 2.6中,內(nèi)核已經(jīng)可以
搶占,因而實時性得到了加強。但是內(nèi)核中仍有大量的不可搶占區(qū)域, 如由自旋鎖spinlock保護的臨界區(qū),以及一些失效搶占的臨界區(qū)。另一個影響Linux實時性的因素就是關(guān)中斷,同步操作中使用的關(guān)中斷指令增大了中斷延遲,這樣很多由中斷驅(qū)動的實時任務(wù)得不到及時的執(zhí)行,系統(tǒng)的實時性能得不到保障。因此提高Linux的可搶占性,改進其中斷機制有利于改善內(nèi)核的實時性。
本文詳細闡述了這些措施的原理并基于標準Linux2.6內(nèi)核加以實現(xiàn),最后通過測試,驗證了此改進方法的效果。
1 中斷線程化
1.1 Linux中斷對內(nèi)核實時性的影響
中斷處理是由內(nèi)核執(zhí)行的最敏感的任務(wù)之一,當內(nèi)核正打算去做一些別的事情時,中
斷隨時會到來,中斷當前的任務(wù)進而執(zhí)行中斷處理程序。因此內(nèi)核的目標就是讓中斷盡可能快的處理完,盡其所能把更多的處理工作向后推遲。為此Linux把中斷的處理分成上半部分和下半部分。上半部分立即執(zhí)行,下半部分將喚醒相應(yīng)的和中斷處理相關(guān)的進程稍后執(zhí)行。雖然這種機制使得中斷處理變得更加高效和易于維護,但是對于系統(tǒng)如果有嚴重的網(wǎng)絡(luò)負載或其他I/O負載時,中斷將非常頻繁,內(nèi)核當前的實時任務(wù)會被不停中斷,這對于Linux的實時應(yīng)用來說是不可接受的。
另外,Linux為了使內(nèi)核同步而采用了關(guān)中斷,在內(nèi)核的關(guān)中斷區(qū)域,中斷是被屏蔽的。即使此時有通過中斷驅(qū)動的實時任務(wù)也得不到響應(yīng),增加了實時任務(wù)的中斷延遲。實時化后的Linux中,自旋鎖被互斥鎖取代,而中斷處理代碼中大量運用了自旋鎖,中斷處理代碼就有可能因為得不到鎖而需要被掛到該鎖的等待隊列上去。但是只有可調(diào)度的進程才可這么做,如果中斷處理代碼仍然使用原來的自旋鎖,那么互斥鎖取代自旋鎖改進內(nèi)核實時性的努力將大打折扣。
線程化的中斷管理可以有效的解決這些問題。中斷線程化后,中斷將作為內(nèi)核線程運行而且賦予不同的實時優(yōu)先級,實時任務(wù)可以有比中斷線程更高的優(yōu)先級,這樣,實時任務(wù)就可以作為最高優(yōu)先級的執(zhí)行單元來運行,即使在嚴重負載下仍有實時性保證。另一方面中斷處理線程也可以因為在內(nèi)核同步中得不到鎖而掛載到鎖的等待隊列中。很多關(guān)中斷就不必真正的禁止硬件中斷了,而是禁止內(nèi)核進程搶占,這樣就可以減小中斷延遲。
1.2 設(shè)計與實現(xiàn)
Linux提供了kthread_create創(chuàng)建內(nèi)核線程,該內(nèi)核線程在內(nèi)核空間執(zhí)行,因此在調(diào)度時沒有用戶空間和內(nèi)核空間切換,使得其運行更為高效。中斷線程化要做的工作是創(chuàng)建中斷線程以及中斷的處理。中斷線程是在系統(tǒng)初始化或者調(diào)用requestirq函數(shù)時通過kthread_create函數(shù)創(chuàng)建的。其過程等同于如下功能代碼:
for (i = 0; i NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
if (desc->action !(desc->status IRQ_NODELAY))
desc->thread = kthread_create();
}
對于非緊急中斷,kthread_create為其創(chuàng)建一個內(nèi)核線程,并且根據(jù)中斷號為其賦予一定的靜態(tài)實時優(yōu)先級和設(shè)置其調(diào)度策略。中斷到來后,內(nèi)核并不是直接進入中斷服務(wù)函數(shù),而是通過設(shè)置調(diào)度標志告知內(nèi)核,內(nèi)核調(diào)度程序比較該中斷線程的優(yōu)先級和當前運行任務(wù)的優(yōu)先級,作出調(diào)度決策。因此當前正在運行的高優(yōu)先級的實時任務(wù)不會受中斷的太大的影響,保證了實時任務(wù)運行的可靠性和準確性,中斷線程將會其他合適的時刻被調(diào)度執(zhí)行,而且Linux2.6內(nèi)核的O(1)調(diào)度機制也不會因為內(nèi)核線程數(shù)的增加在調(diào)度時間上額外增加調(diào)度開銷。對于緊急的中斷(比如時鐘中斷),內(nèi)核保持原來的中斷處理方式,而不為其創(chuàng)建中斷線程,這樣保證了緊急中斷的快速響應(yīng)。
2 自旋鎖改互斥鎖
2.1 新的自旋鎖設(shè)計
為了同步不同內(nèi)核控制路徑對共享資源的訪問,標準Linux內(nèi)核提供了很多內(nèi)核同步原語,其中自旋鎖spinlock是一種廣泛應(yīng)用于可搶占內(nèi)核,SMP環(huán)境下的內(nèi)核同步機制。在spinlock的保持期間,內(nèi)核是搶占失效的。被自旋鎖保護的區(qū)域稱為臨界區(qū),內(nèi)核中大量使用了spinlock,存在大量的內(nèi)核不可搶占的臨界區(qū),這將嚴重影響系統(tǒng)的實時性,我們使用新的實時互斥鎖rt_mutex來替換spinlock,即讓臨界區(qū)內(nèi)內(nèi)核可搶占。其結(jié)構(gòu)體如下:
typedef struct {
struct rt_mutex lock;
unsigned int break_lock;
} spinlock_t;
struct rt_mutex {
…
raw_spinlock_t wait_lock;
struct plist wait_list;
struct thread_info *owner;
……
}
類型raw_spinlock_t就是原來的spinlock_t。新的自旋鎖還是使用spinlock_t來標記。在結(jié)構(gòu)struct rt_mutex中的wait_list字段就是優(yōu)先級化的等待隊列。Owner字段為擁有該鎖的進程的環(huán)境信息。mutex比spinlock優(yōu)越的地方有這么幾點:(1)當遇到一個鎖住的臨界區(qū)時,任務(wù)被掛起到鎖的優(yōu)先級等待隊列wait_list中,臨界區(qū)解鎖時等待任務(wù)被激活。(2)內(nèi)核將一個鎖住的臨界區(qū)和一個任務(wù)關(guān)聯(lián),當獲得互斥鎖時將任務(wù)的標識存入鎖中。(3)臨界區(qū)可以在被保護的同時不禁止搶占。(4)在被鎖住的臨界區(qū)中可以實現(xiàn)優(yōu)先級繼承。
2.2 實時互斥鎖的操作
并不是所有內(nèi)核中的自旋鎖都可以轉(zhuǎn)換為互斥鎖的,一些底層的臨界區(qū)必須是不可搶占的,所以必須由不可搶占的自旋鎖保護,比如:保護硬件寄存器的鎖、調(diào)度器的運行隊列鎖、和其它不可搶占自旋鎖嵌套的鎖。實時內(nèi)核中,不可搶占的自旋鎖與RT互斥鎖共存,不可搶占的自旋鎖被重命為raw_spinlock_t。spin_lock函數(shù)利用gcc的內(nèi)嵌技術(shù)根據(jù)鎖的類型通過預處理選擇具體的鎖執(zhí)行函數(shù)。預處理器使用__builtin_types_compatible函數(shù),由宏TYPE_EQUAL調(diào)用:
#define TYPE_EQUAL(lock, type)
__builtin_types_compatible_p(typeof(lock), type *)
函數(shù)映射結(jié)構(gòu)PICK_OP是允許兩種鎖共存的機制,PICK_OP在編譯階段將鎖操作轉(zhuǎn)化為合適的種類(mutex或者spinlock):
#define PICK_OP(type, optype, op, lock)
do {
if (TYPE_EQUAL((lock), type))
_raw_##optype##op((type *)(lock));
else if (TYPE_EQUAL(lock, spinlock_t))
_spin##op((spinlock_t *)(lock));
else __bad_spinlock_type();
} while (0)
#define spin_lock(lock)
PICK_OP(raw_spinlock_t, spin, _lock, lock)
根據(jù)宏處理的結(jié)果,如果類型為spinlock_t,將運行函數(shù)_spin_lock,而如果類型為raw_spinlock_t,將運行函數(shù)_raw_spin_lock。對于實時rt_mutex獲取,當前任務(wù)由于優(yōu)先級高如果可以搶占該鎖,則把鎖的先前擁有者添加到該鎖的等待隊列中并且在新的鎖擁有者的task_struct中標記等待該鎖的所有任務(wù);而如果不能獲得鎖,則把當前任務(wù)放到鎖的優(yōu)先級等待隊列中,直到鎖的擁有者釋放該鎖,喚醒等待隊列中最高優(yōu)先級的任務(wù)進入臨界區(qū)。為了防止優(yōu)先級反轉(zhuǎn),可以改變鎖的當前擁有者的優(yōu)先級為鎖的等待隊列中任務(wù)的最高優(yōu)先級。
實時互斥鎖的應(yīng)用使得高優(yōu)先級的任務(wù)通過搶占鎖可以進入臨界區(qū),這樣內(nèi)核的不可搶占區(qū)的數(shù)量和范圍都大大縮小,內(nèi)核可搶占性有了很大的提高,充分減小了實時高優(yōu)先級任務(wù)的搶占延遲。
3 改進后的內(nèi)核實時性能測試
對Linux進行了實時化改造后,需要對其進行測試來確定系統(tǒng)是否符合實際應(yīng)用的需要。實時性能測試主要包括測試內(nèi)核的最大中斷延遲和任務(wù)調(diào)度時間延遲兩個重要的指標。
中斷延遲反映的是中斷信號發(fā)生到內(nèi)核對中斷做出響應(yīng)所需要的時間,通過利用并口的7號中斷,短路并口的9-10腳,其中10腳為中斷引腳,9口為并口數(shù)據(jù)口的高位,往IO口0X378寫8就可以引發(fā)中斷,然后在中斷服務(wù)程序里往0x378寫0就會重新拉低中斷引腳電平。用高精度示波器測量這個脈沖的寬度來測得中斷延遲時間。對于賽揚650MHz,內(nèi)存128M的工控平臺,負載為拷貝CF卡情況下,標準Linux2.6內(nèi)核最長中斷延遲為240us,而改進后的實時內(nèi)核最長中斷延遲為16us。
采用開源軟件LMbench3.0測試系統(tǒng)調(diào)度延遲時間。測試機器配置為:CPU ,Intel P4 2.8GHz,內(nèi)存512M,負載情況為拷貝硬盤、訪問網(wǎng)絡(luò)。標準Linux2.6內(nèi)核調(diào)度延遲為1038us,而改進后的內(nèi)核的調(diào)度延遲為140us。
可見改進后的內(nèi)核的最長中斷延遲和調(diào)度延遲比標準內(nèi)核要短很多,內(nèi)核的實時性能有了很大的提高。
4 結(jié)束語
針對標準Linux2.6內(nèi)核由于中斷關(guān)閉時間過長、內(nèi)核臨界不可搶占區(qū)過大而多,明顯存在實時性能方面的不足,提出了改進其實時性的方法,實現(xiàn)了實時Linux內(nèi)核的中斷線程化以及新的內(nèi)核同步自旋鎖,使其具有更短的中斷延遲和調(diào)度延遲。
本文作者的創(chuàng)新點:在標準的Linux2.6可搶占內(nèi)核上對其中斷處理機制進行線程化改造,大大的減小了中斷響應(yīng)時間。另外對內(nèi)核同步機制采用新的自旋鎖使得內(nèi)核可搶占性能更加優(yōu)異,改進后的內(nèi)核可以適用于對Linux內(nèi)核實時性要求更高的場合。 linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評論