嵌入式實(shí)時(shí)Linux的技術(shù)研究
——
目前嵌入式操作系統(tǒng)主要有Palm OS,WindowsCE,EPOC,LinuxCE,QNX.ECOS,LYNX等,但這些系統(tǒng)一般都價(jià)格昂貴、不具有良好的可移植性。由于linux具有適應(yīng)于多種CPU和多種硬件平臺(tái)、性能穩(wěn)定、裁剪性能好,開(kāi)發(fā)和使用都很容易等特點(diǎn),越來(lái)越多的人傾向于將Linux嵌入到移動(dòng)計(jì)算平臺(tái)、信息家電、媒體手機(jī)及其它產(chǎn)品中去。這同時(shí)也對(duì)Linux的實(shí)時(shí)性提出了更高的要求。
我們參加開(kāi)發(fā)的上海數(shù)字技術(shù)中心的電子警察系統(tǒng)就是這樣一個(gè)基于PC平臺(tái)的嵌入式系統(tǒng)。它的主要功能是安裝在十字路口上監(jiān)視闖紅燈的汽車,錄制這些鏡頭,并適時(shí)通過(guò)網(wǎng)絡(luò)傳回總監(jiān)控中心。該系統(tǒng)原來(lái)采用Vxworks作為運(yùn)作平臺(tái),然而由于Vxworks的昂貴,且只能安裝一臺(tái)機(jī)器,因此將Linux改制成嵌入式的、具有一定實(shí)時(shí)性的系統(tǒng)平臺(tái)。
1 硬件需求
考慮到實(shí)際的系統(tǒng)功能和系統(tǒng)開(kāi)發(fā)的難度,我們決定選擇X86體系作為平臺(tái),底板采PCM-5864/L板,CPU選擇Intel的P55C系列,同時(shí)它也支持PC104接口。I/O方面,PCM-5864/L支持EIDE、FDD、Keyboard、Mouse、RS-232、USB等接口,同時(shí)也集成了10Mbps/l00Mbps網(wǎng)卡。另外PCM-5864/L還集成了顯卡,并提供36位TTL的LCD接口、LVDS接口和Video-in/TV-out接口。由于電子磁盤體積小、抗震性能也較好的特點(diǎn),我們選用M-Systems公司的DiskOnChip2000作為存儲(chǔ)設(shè)備,這樣比較適合該系統(tǒng)戶外作業(yè)的特性。Doc2000還提供了安裝的工具包、Linux下的驅(qū)動(dòng)程序和對(duì)不同版本Linux內(nèi)核的Patch。安裝時(shí)只需對(duì)Linux內(nèi)核和Lilo進(jìn)行相應(yīng)修改即可。這樣的硬件選擇使系統(tǒng)開(kāi)發(fā)的難度大大降低了。
2 嵌入Linux主要解決的幾個(gè)問(wèn)題
由于采用X86體系作為平臺(tái),大大降低了嵌入化Linux的難度,主要要考慮的問(wèn)題可分為以下幾個(gè)方面:
2.1 非虛擬內(nèi)存
Linux采用虛擬內(nèi)存技術(shù),當(dāng)數(shù)據(jù)溢出內(nèi)存時(shí),可以將其交換到磁盤交換空間巾去,這對(duì)程序員來(lái)說(shuō)是不可見(jiàn)的。然而,普通的嵌入式系統(tǒng)不需要這種強(qiáng)大的功能。實(shí)際上,可能不希望它在實(shí)時(shí)的關(guān)鍵系統(tǒng)里,因?yàn)樗鼤?huì)帶來(lái)無(wú)法控制的時(shí)問(wèn)因素。因此,考慮將虛擬內(nèi)存的功能去掉。然而,清除Linux的虛擬內(nèi)存代碼非常費(fèi)事,我們采用一種迂回的方法,即將交換空間的大小設(shè)置為零。這樣,如果你寫的程序比實(shí)際的內(nèi)存大,系統(tǒng)就會(huì)當(dāng)作你的運(yùn)行用盡了交換空間來(lái)處理;這個(gè)程序?qū)⒉粫?huì)運(yùn)行,或者malloc將會(huì)失靈。這只是一種臨時(shí)的解決方案,系統(tǒng)中存在許多冗余未清除的代碼,如果進(jìn)一步改進(jìn)可以縮小系統(tǒng)體積。
2.2 處理文件系統(tǒng)
許多嵌入式系統(tǒng)沒(méi)有磁盤或者文件系統(tǒng),Linux不需要它們也能運(yùn)行,這樣可以在系統(tǒng)啟動(dòng)時(shí)就將操作系統(tǒng)和預(yù)先編好的應(yīng)用程序全都加載到內(nèi)存中去。然而.考慮到以后的擴(kuò)展,我們分析了Linux當(dāng)前的文件系統(tǒng),將與系統(tǒng)和程序運(yùn)行相關(guān)的文件析取出來(lái),移植到Doc上面.這樣就能為應(yīng)用程序的開(kāi)發(fā)人員提供更方便的接口。
2.3 設(shè)置啟動(dòng)(Lilo和BIOS)
當(dāng)PC系統(tǒng)啟動(dòng)時(shí),由BIOS執(zhí)行了一些低水平的CPU初始化和其它硬件的配置,然后辨認(rèn)哪個(gè)磁盤里有操作系統(tǒng),把操作系統(tǒng)復(fù)制到RAM并且轉(zhuǎn)向它。在PC上運(yùn)行的Linux依靠PC的BIOS來(lái)提供這些配置和OS加戟功能。由于選擇了X86系列作為平臺(tái),同樣可以通過(guò)設(shè)置BIOS來(lái)設(shè)定由Doc啟動(dòng)。我們將內(nèi)核安裝在Doc上。同時(shí)修改目錄/etc下的lilo.config文件,使Lilo記錄內(nèi)核代碼在存儲(chǔ)設(shè)備上的位置,然后將Lilo安裝在Doc上。這樣當(dāng)系統(tǒng)啟動(dòng)時(shí).BIOS指定Doc為啟動(dòng)設(shè)備,這時(shí)Lilo被執(zhí)行。它指出Linux內(nèi)核的位置,加載內(nèi)核。
2.4 設(shè)備驅(qū)動(dòng)程序的編寫
由于采用通用PC平臺(tái),有許多驅(qū)動(dòng)程序都不需要自己再編寫了。系統(tǒng)中,唯一需要編寫驅(qū)動(dòng)程序的設(shè)備是上海數(shù)字中心的數(shù)據(jù)采集卡.該卡采用的是Bt848芯片。Bt848是一塊很常用的芯片.許多驅(qū)動(dòng)程序都非常相近,因此編寫它的驅(qū)動(dòng)程序有很好的例子可以參考,相應(yīng)的,編寫應(yīng)用程序只需調(diào)用驅(qū)動(dòng)程序提供的接口即可,如同操作文件一樣。
3 Linux的實(shí)時(shí)性分析
以上簡(jiǎn)要介紹了實(shí)現(xiàn)Linux的嵌入化過(guò)程,在嵌入化Linux的同時(shí),還考慮提高它的實(shí)時(shí)性。于是著手分析Linux的實(shí)時(shí)性能和存在的不足。
概括來(lái)講,影響操作系統(tǒng)實(shí)時(shí)性能的主要有3個(gè)方面:
(1)外部中斷管理
我們知道,外部中斷發(fā)生時(shí),操作系統(tǒng)調(diào)用中斷處理程序.進(jìn)入核心態(tài)。為了保證系統(tǒng)執(zhí)行的正確性,要求內(nèi)核狀態(tài)不重入,也即保證這部分關(guān)鍵代碼執(zhí)行結(jié)束之前不被打斷。因此,這時(shí)進(jìn)入關(guān)中模式,這是外部中斷管理中影響Linux性能的一個(gè)關(guān)鍵的地方。在這段時(shí)間內(nèi),操作系統(tǒng)負(fù)責(zé)將中斷發(fā)送到相應(yīng)的設(shè)備驅(qū)動(dòng)程序去處理,系統(tǒng)不能進(jìn)行其它任何工作,為了減少這個(gè)過(guò)程損耗的時(shí)間,Linux內(nèi)核利用底半處理過(guò)程(bottom-half-handler)幫助實(shí)現(xiàn)中斷的快速處理。在Linux設(shè)備驅(qū)動(dòng)程序中,往往將最關(guān)鍵最迅速的部分處理完成之后,將剩余部分任務(wù)放置到隊(duì)列中。當(dāng)中斷響應(yīng)完成后.再執(zhí)行剩余部分的任務(wù)。在Linux中,主要設(shè)置了以下幾個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)標(biāo)志未完成的任務(wù)。
enum{
TIMER_BH=0,CONSOLE_BH,TQUEUE_BH,DIGI_BH,SERIAL_BH,RISCOM8_BH,SPECIALIX_BH,
ESP_BH,NET_BH,SCSI_BH,IMMEDIATE_BH,KEYBOARD_BH,CYCLADES_BH,CM206_BH,
JS_BH,MACSERIAL_BHISICOM_BH
};
上面每一項(xiàng)標(biāo)識(shí)未完成任務(wù)的隊(duì)列類型,不同隊(duì)列的任務(wù)輕重緩急不同。
extern unsigned long bh_active;
extern unsigned long bh_mask;
extern void(*bh_base[32])(void);
bh_base代表的指針數(shù)組中可包含32個(gè)不同的底半處理過(guò)程。bh_mask和札bh_active的數(shù)據(jù)位分別代表對(duì)應(yīng)的底半處理過(guò)程是否安裝和激活。如果bh_mask的第N位為1.則說(shuō)明bh_base數(shù)組的第N個(gè)元素包含某個(gè)底半處理過(guò)程的地址;如果bh_active的第N位為1.則說(shuō)明必須由調(diào)度程序在適當(dāng)?shù)臅r(shí)候調(diào)用第N個(gè)底半處理過(guò)程。這些數(shù)據(jù)結(jié)構(gòu)的設(shè)置一般是在外部設(shè)備初始化和中斷處理函數(shù)運(yùn)行時(shí)進(jìn)行的。如:在serialc中進(jìn)行serial設(shè)備的初始化,它調(diào)用語(yǔ)句init_bh(SERIAL_BH,do_serial_bh);來(lái)設(shè)置bh_base[]數(shù)組中相應(yīng)于SERIAL_BH的那一項(xiàng)。又如:在serial設(shè)備的處理程序中通過(guò)語(yǔ)句queue_task(&info->tqueue,&tq_serial);將不是很緊急的任務(wù)放入tq_serial隊(duì)列中,等中斷處理函數(shù)結(jié)束,由bh_base中注冊(cè)的底半處理程序處理隊(duì)列中的任務(wù)。
中斷管理的第二個(gè)關(guān)鍵部分即是系統(tǒng)是否允許中斷嵌套的能力,也就是說(shuō),當(dāng)響應(yīng)一個(gè)中斷時(shí),是否允許其它更高優(yōu)先級(jí)的中斷打斷,等更高優(yōu)先級(jí)的中斷處理完畢,是否還能恢復(fù)原來(lái)中斷處理的現(xiàn)場(chǎng)。通過(guò)這項(xiàng)功能,系統(tǒng)設(shè)計(jì)者可以指示外部中斷的優(yōu)先級(jí),從而確保高優(yōu)先級(jí)的任務(wù)能及時(shí)處理。Linux允許中斷嵌套,它是利用外部中斷管理器來(lái)設(shè)置中斷的優(yōu)先級(jí)的。在Linux的中斷處理程序的啟動(dòng)過(guò)程中,它一般調(diào)用語(yǔ)句mask_and_ack_8259A(irq);來(lái)設(shè)置8259中的int_mask寄存器.使優(yōu)先級(jí)比此中斷低的中斷不能發(fā)生。在中斷處理程序離開(kāi)時(shí),調(diào)用enable_8259A_irq(irq)來(lái)改回8259中int_mask寄存器原來(lái)的值。因此,intr類中斷的優(yōu)先級(jí)由硬件8259來(lái)決定.
由此可見(jiàn),Linux的中斷管理部分具有高效的特點(diǎn),已經(jīng)可以滿足許多軟實(shí)時(shí)任務(wù)的要求。
(2)進(jìn)程搶先調(diào)度
在許多控制系統(tǒng)中,實(shí)時(shí)控制軟件是非常簡(jiǎn)單的,可以直接寫入中斷處理程序中與一個(gè)特定的中斷聯(lián)系起來(lái)。還有一些就不那么簡(jiǎn)單了,必須開(kāi)啟專門的用戶進(jìn)程為它服務(wù)。
這時(shí)當(dāng)這個(gè)高優(yōu)先級(jí)的進(jìn)程提交時(shí),如有其它進(jìn)程正在運(yùn)行,它就必須打斷正在運(yùn)行的進(jìn)程。若正在運(yùn)行的進(jìn)程運(yùn)行在用戶態(tài),系統(tǒng)一般允許它被打斷且執(zhí)行其它優(yōu)先權(quán)高的進(jìn)程,若正在運(yùn)行的進(jìn)程運(yùn)行在系統(tǒng)態(tài),則此時(shí)是否允許被打斷決定了系統(tǒng)是搶先式的還是非搶先式的。
Linux就是一個(gè)非搶先式的操作系統(tǒng),在用戶執(zhí)行系統(tǒng)調(diào)用時(shí),不允許其它進(jìn)程的調(diào)度,這樣就影響了系統(tǒng)的響應(yīng)度。一個(gè)真正的搶先式的操作系統(tǒng)允許正在系統(tǒng)狀態(tài)下的當(dāng)前進(jìn)程被打斷,然后進(jìn)程切換回來(lái)時(shí)還能繼續(xù)從剛才的執(zhí)行點(diǎn)繼續(xù)下去。但某些關(guān)鍵部分的代碼段。系統(tǒng)必須保證其原子性,并防止重入。通常有如下幾種方法:
在關(guān)鍵代碼斷前關(guān)閉中斷,等其執(zhí)行完畢之后再將中斷打開(kāi);
設(shè)計(jì)一個(gè)信號(hào)量.在關(guān)鍵代碼段之前加鎖,在其后解鎖;
在系統(tǒng)代碼中保證安全的地方加入切換進(jìn)程的代碼switch(),防止該進(jìn)程長(zhǎng)久占用CPU,允許調(diào)度其它進(jìn)程;
在關(guān)鍵代碼段加入一個(gè)switchaccept標(biāo)志,開(kāi)始該代碼段時(shí)。將此標(biāo)志置為否.離開(kāi)時(shí)再置回原來(lái)的值.這樣在執(zhí)行該段代碼時(shí),即使進(jìn)程調(diào)度器被激活,它也會(huì)先檢查此標(biāo)志。若為否,則返回,并不進(jìn)行進(jìn)程切換。
(3)進(jìn)程調(diào)度策略
第三個(gè)影響系統(tǒng)響應(yīng)速度的關(guān)鍵部分就是進(jìn)程調(diào)度的策略。對(duì)于一個(gè)實(shí)時(shí)性能強(qiáng)的操作系統(tǒng)來(lái)說(shuō),系統(tǒng)必須規(guī)定不同進(jìn)程的優(yōu)先級(jí),并把優(yōu)先級(jí)作為唯一的進(jìn)程選擇的標(biāo)準(zhǔn)。Linux的后期版本參照Posixl.b標(biāo)準(zhǔn),在某些方面已經(jīng)具備了一些實(shí)時(shí)操作系統(tǒng)的特性。Linux有兩種類型的進(jìn)程:一般進(jìn)程和實(shí)時(shí)進(jìn)程,它可以通過(guò)sched_setscheduler系統(tǒng)調(diào)用設(shè)置實(shí)時(shí)進(jìn)程。實(shí)時(shí)進(jìn)程比所有一般進(jìn)程的優(yōu)先級(jí)高,Linux設(shè)置實(shí)對(duì)進(jìn)程的權(quán)重為它的counter值加1000;設(shè)置一般進(jìn)程的權(quán)重為counter。因此,實(shí)時(shí)進(jìn)程總會(huì)被認(rèn)為是最值得運(yùn)行的進(jìn)程。
然而,Linux核心的設(shè)計(jì)主要集中在應(yīng)用程序的吞吐量上。追求吞吐量的必然結(jié)果,就是Linux調(diào)度器運(yùn)用一種"公平共享"的策略保證所有的進(jìn)程得到平均的CPU資源。而且,Linux的進(jìn)程調(diào)度器只是簡(jiǎn)單地將標(biāo)有實(shí)時(shí)標(biāo)志的進(jìn)程的權(quán)重加1000,至于實(shí)時(shí)進(jìn)程間的輕重緩急還沒(méi)有周密的完整的設(shè)計(jì)。因此,Linux的進(jìn)程調(diào)度器還遠(yuǎn)不能稱作是一個(gè)真正的實(shí)時(shí)進(jìn)程凋度器。
4 擬采用的策略
根據(jù)以上分析的特點(diǎn),我們決定主要從以下4個(gè)方面來(lái)修改Linux的核心代碼。
(1)在內(nèi)核中插入搶先點(diǎn) 由于Linux是一個(gè)非搶先式的操作系統(tǒng)。因此當(dāng)一個(gè)實(shí)時(shí)進(jìn)程提交時(shí),很可能因?yàn)楫?dāng)前的進(jìn)程正處于核心態(tài)不能被打斷而不能得到及時(shí)的處理。因此有必要在Linux內(nèi)核中插入搶先點(diǎn),使實(shí)時(shí)進(jìn)程得到處理。根據(jù)上一節(jié)分析的特點(diǎn),太體有4種方法可供選擇。權(quán)衡這4種方法的利弊,我們決定采用第4種方法,即在關(guān)鍵代碼段加入一個(gè)switchaccept標(biāo)志,開(kāi)始該代碼段時(shí),將此標(biāo)志置為否.離開(kāi)時(shí)再置回原來(lái)的值。這種方法比采甩semaphore的好處是,如果采用許多種semaphore的話.要考慮是否會(huì)產(chǎn)生死鎖的問(wèn)題。比采用鎖中斷的好處是.將中斷鎖住將丟失中斷,而這樣不會(huì)。而以固定的周期加switch語(yǔ)句顯然有失靈活性。這樣.采用這種方法,需要我們分析Linux所有系統(tǒng)調(diào)用的代碼,畫出其結(jié)構(gòu)流程圖。分析出哪些部分是關(guān)鍵部分,也即不允許置入的部分。在關(guān)鍵代碼前后更改switchaccept標(biāo)志。這項(xiàng)工作比較艱巨。同時(shí)修改進(jìn)程調(diào)度器,使其判斷switchaccept標(biāo)志來(lái)決定是否執(zhí)行進(jìn)程切換。
(2)修改進(jìn)程調(diào)度器Linux的進(jìn)程調(diào)度器雖然已經(jīng)具有一定的實(shí)時(shí)性能,但還遠(yuǎn)遠(yuǎn)達(dá)不到真正實(shí)時(shí)調(diào)度器的標(biāo)準(zhǔn),因此需要修改其進(jìn)程調(diào)度器,必要的話可讓Linux運(yùn)行在兩種模式下,實(shí)時(shí)模式和分時(shí)模式。可設(shè)計(jì)一些相關(guān)的系統(tǒng)調(diào)用,并在實(shí)時(shí)進(jìn)程提交時(shí),將系統(tǒng)轉(zhuǎn)化為實(shí)時(shí)模式,當(dāng)實(shí)時(shí)進(jìn)程結(jié)束時(shí),再轉(zhuǎn)化為分時(shí)模式。
(3)Linux的中斷管理根據(jù)前面分析過(guò)的,Linux的中斷管理及時(shí)地將緊要的任務(wù)完成后,將其余不重要的緩慢的任務(wù)放置在任務(wù)隊(duì)列中,等到系統(tǒng)空閑(cpu idle())或系統(tǒng)調(diào)用等返回時(shí)再完成這些任務(wù),這樣就提高了系統(tǒng)的響應(yīng)速度,同時(shí),Linux還支持中斷嵌套。因此,不再對(duì)其作太大改動(dòng)。
(4)鎖定內(nèi)存 在本項(xiàng)目的規(guī)劃中本打算實(shí)現(xiàn)Linux鎖內(nèi)存的功能,使優(yōu)先權(quán)高的進(jìn)程在內(nèi)存中的數(shù)據(jù)不被換出,從而提高實(shí)時(shí)進(jìn)程的運(yùn)行速度。然而,在分析了Linux代碼后,發(fā)現(xiàn)后來(lái)版本的Linux已通過(guò)系統(tǒng)調(diào)用sys mlock實(shí)現(xiàn)了此項(xiàng)功能。
5 結(jié)束語(yǔ)
采用上述方法修改了內(nèi)核代碼后,由于每個(gè)修改方案都是有一定的代價(jià)的,它在增加了系統(tǒng)響應(yīng)速度的同時(shí)也在某種程度上降低了系統(tǒng)的整體效率,比如說(shuō)將內(nèi)核設(shè)置成可搶先的,在進(jìn)程頻繁的切換過(guò)程中也要消耗一定的cpu處理時(shí)間。因此,還需要對(duì)各種解決方案進(jìn)行測(cè)試、比較。另外,為了減少嵌入式Linux自身的長(zhǎng)度,在存儲(chǔ)管理部分對(duì)虛擬內(nèi)存也應(yīng)作進(jìn)一步的處理。
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評(píng)論