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