向嵌入式Linux移植實(shí)時(shí)設備驅動(dòng)程序
Linux暴風(fēng)雨般地占領(lǐng)了嵌入式系統市場(chǎng)。根據工業(yè)分析家分析,大約1/3到1/2的新的32位和64位嵌入式系統設計采用了Linux。嵌入式Linux已經(jīng)在很多應用領(lǐng)域顯示出優(yōu)勢,比如SOHO家庭網(wǎng)絡(luò )和成像/多功能外設,并在以下幾方面具備巨大的跨越式發(fā)展前景:(NAS/SAN)存儲,家庭數字娛樂(lè )(HDTV/PVR/DVR/STB)和手持設備/無(wú)線(xiàn)設備,特別是數字移動(dòng)電話(huà)。
本文引用地址:http://dyxdggzs.com/article/149225.htm新的嵌入式Linux應用不會(huì )象掌握在智慧和工藝之神-羅神手中那樣,會(huì )突然從開(kāi)發(fā)者的頭腦中爆發(fā)出來(lái)。大量的項目必須采用數千行的,甚至數百萬(wàn)行的過(guò)去的現成代碼。成百上千的嵌入式項目已經(jīng)成功地將其它平臺的現成代碼移植到Linux之上,比如WindRiverVxWorks和pSOS,VRTX,Nucleus和其它RTOS,這些移植工作現在仍然有價(jià)值和現實(shí)意義。
到目前為止,大多數的關(guān)于移植舊的RTOS應用到嵌入式Linux的文獻,已經(jīng)在關(guān)注RTOS接口(API),任務(wù),調度模式和怎樣將他們映射到相應的用戶(hù)空間去。在嵌入式程序的密集I/O空間中,同樣重要的是,將RTOS的應用硬件接口代碼向具有更加規范化模式的Linux設備啟動(dòng)程序的移植。
本文將縱覽幾種常用的內存映射I/O方法,它們經(jīng)常出現于舊的嵌入式應用中。它們涵蓋的范圍,包括從對中斷服務(wù)例程的特殊使用和用戶(hù)線(xiàn)程對硬件訪(fǎng)問(wèn),到出現于有些ROTS中的半規范化驅動(dòng)程序模型。它對于移植RTOS代碼到規范化模式的Linux設備啟動(dòng)程序具有啟發(fā)性,并且介紹了一些方法。特別地,本文會(huì )重點(diǎn)討論和比較RTOS代碼中的內存映射,Linux基于I/O調度隊列的移植,和重新定義RTOSI/O,以便在本地Linux驅動(dòng)程序和守護進(jìn)程里應用。
RTOSI/O概念
“不規范”是能夠描述大多數在基于RTOS系統里的I/O的最佳詞語(yǔ)。大多數RTOS針對較早的無(wú)MMU的CPU而設計,忽略了內存管理,即使當MMU問(wèn)世也是這樣,不區分物理地址和邏輯地址。大多數RTOS還全部在特權態(tài)(系統模式)運行,表面上看增強了性能。像這樣,全部的RTOS應用和系統代碼都能夠訪(fǎng)問(wèn)整個(gè)機器地址空間,內存映射設備和I/O指令。實(shí)際上,將RTOS應用程序代碼同驅動(dòng)程序代碼區分開(kāi)非常困難,即使它們是有差別的。
這個(gè)不規范的結構導致了I/O的特殊實(shí)現。在很多情況下,完全缺乏對一種設備驅動(dòng)程序模型的認同。根據這種工作的平等和沒(méi)有分層的特性,回顧在基于RTOS軟件中使用的一些重要概念和實(shí)踐非常有指導意義。
在線(xiàn)內存映射訪(fǎng)問(wèn)
當在上個(gè)世紀八十年代中期商業(yè)化的RTOS產(chǎn)品可以買(mǎi)到的時(shí)候,大多數嵌入式軟件包含巨大的主循環(huán),主循環(huán)帶有針對嚴格時(shí)間操作的注冊I/O和中斷服務(wù)例程。開(kāi)發(fā)人員將RTOS和執行程序設計進(jìn)他們的項目,主要為了加強同時(shí)性和幫助多任務(wù)同步,但是避開(kāi)其它任何有“妨礙“的構造。同樣地,即使一個(gè)RTOS提供了I/O調用形式方法,嵌入式程序員繼續使用直接的I/O操作:
#defineDATA_REGISTER0xF00000F5
chargetchar(void){
return(*((char*)DATA_REGISTER));/*readfromport*/
}
voidputchar(charc){
*((char*)DATA_REGISTER)=c;/*writetoport*/
}
多數受過(guò)訓練的開(kāi)發(fā)者常常將這樣的直接I/O代碼從硬件代碼獨立分離開(kāi)。但是我還曾遇見(jiàn)大量的意大利面條式的I/O處理代碼。
當普遍深入使用直接內存映射I/O的時(shí)候,對Linux開(kāi)始接觸的嵌入式開(kāi)發(fā)人員總是面臨將所有的這類(lèi)代碼移植到用戶(hù)空間,將定義寄存器地址的#define語(yǔ)句轉換成mmap()調用。這種處理方法對于一些種類(lèi)的原型很好,但是不能支持中斷處理,限制了實(shí)時(shí)響應,特別不安全,不適合作為商業(yè)發(fā)布。
RTOS中斷服務(wù)例程
在Linux中,中斷服務(wù)專(zhuān)屬于內核的范圍。在一個(gè)RTOS中,中斷服務(wù)例程代碼是自由形態(tài)而且與應用程序代碼沒(méi)有區別(不外乎返回序列)。很多RTOS提供系統調用或者宏,來(lái)讓代碼自己檢測它自己的切換點(diǎn)(比如WindRiverVxWorks的intContext())。中斷服務(wù)例程通常也使用標準的庫函數,伴隨著(zhù)可重入性和可移植性問(wèn)題。大多數RTOS支持注冊中斷服務(wù)例程代碼,中斷仲裁句柄和中斷服務(wù)例程調度。一些非常原始的嵌入式執行程序,僅僅支持在硬件矢量表里插入中斷服務(wù)例程的開(kāi)始地址。即使你試圖直接在用戶(hù)程序空間執行讀和寫(xiě)的操作,你不得不將你的Linux中斷服務(wù)例程放入內核程序空間。
RTOSI/O子系統
大多數RTOS會(huì )提供一個(gè)定制的標準C運行庫(比如pSOS的pREPC),或者可以從獨立軟件開(kāi)發(fā)商的編譯器中選擇打補丁的C庫(libc)同樣可以得到glibc。這樣,在最小化情況下,多數的RTOS支持標準C類(lèi)型I/O的一個(gè)子集(open/close/read/write/ioctl)。大多數情況下,這些調用和從他們衍生出來(lái)的調用可以轉化為圍繞基本I/O的非常薄的封裝程序。有趣的是,因為大多數的?RTOS不支持文件系統,這些平臺不提供針對flash和旋轉媒質(zhì)的抽象文件存儲,常常使用完全不同的代碼和/或者不同的應用程序接口(API)(比如pSOS的pHILE)。
WindRiverVxWorks在這方面比其它多數RTOS平臺做的較好些,提供功能豐富的I/O子集,主要克服了網(wǎng)絡(luò )接口/多媒體接口里的集成和廣泛化障礙。
延時(shí)處理
很多RTOS也支持一種叫”下半部“(bottomhalf)的機制,它針對可中斷和/或者可搶占切換的I/O延時(shí)處理方法。其他RTOS沒(méi)有這樣的機制,但是替代地提供類(lèi)似中斷嵌套的機制來(lái)獲得同樣的效果。
典型RTOS應用I/O架構
下面描述一個(gè)典型的I/O配置(僅僅輸入)和它向主要應用程序傳遞數據的路徑處理過(guò)程依次如下:
*一個(gè)硬件中斷觸發(fā)一個(gè)中斷服務(wù)例程的執行。
*中斷服務(wù)例程做基本的處理和完成本地的輸入操作,或者讓RTOS調度延時(shí)的處理。在一些情況下,延時(shí)處理過(guò)程由在Linux里面被叫做用戶(hù)進(jìn)程來(lái)處理,在這里就是通常的RTOS任務(wù)。
*無(wú)論在何時(shí)何地獲得數據(中斷服務(wù)例程或者延時(shí)切換),準備好的數據被放進(jìn)隊列(RTOS中斷服務(wù)例程能夠訪(fǎng)問(wèn)應用程序隊列通過(guò)應用程序接口(API)和其它進(jìn)程間通信(?IPC),請看下面的API表)。
*一個(gè)或者多個(gè)應用任務(wù)然后從隊列讀消息,來(lái)取出數據。
在傳統的RTOS和Linux之間的典型I/O的比較輸出常常由類(lèi)似的機制來(lái)完成。替代使用write()或者相似的系統調用,一個(gè)或者多個(gè)RTOS應用程序任務(wù),將準備好的數據放進(jìn)隊列。隊列中的數據由以下過(guò)程取出:一個(gè)I/O程序或者響應”準備好發(fā)送”中斷的中斷服務(wù)例程,一個(gè)系統時(shí)鐘,或者其它阻塞在獲取隊列中的應用任務(wù),然后直接執行I/O操作(可以是輪詢(xún),也可以是通過(guò)DMA)。
將RTOSI/O映射進(jìn)Linux
上面描述的基于隊列的生產(chǎn)/消費I/O模型,僅僅是很多種在傳統設計中所采用的特別方法之一。讓我們繼續用這個(gè)直接的例子,來(lái)討論幾種在嵌入式Linux下的實(shí)現:
大規模移植到用戶(hù)空間
對于勉強了解Linux設備驅動(dòng)設計細節,或者非常匆忙的開(kāi)發(fā)者,可能將大多數這樣基于隊列設計程序完整無(wú)缺地移植到用戶(hù)空間。在這種驅動(dòng)程序映射配置中,內存映射的物理I/O口通過(guò)函數mmap()提供的指針可以在用戶(hù)空間操作。
#include
#defineREG_SIZE0x4/*deviceregistersize*/
#defineREG_OFFSET0xFA400000
/*physicaladdressofdevice*/
void*mem_ptr;/*de-referenceformemory-mappedaccess*/
intfd;
fd=open(/dev/mem,O_RDWR);/*openphysicalmemory(mustberoot)*/
mem_ptr=mmap((void*)0x0,REG_AREA_SIZE,PROT_READ+PROT_WRITE,
MAP_SHARED,fd,REG_OFFSET);
/*actualcalltommap()*/
一個(gè)基于進(jìn)程的用戶(hù)線(xiàn)程進(jìn)行與基于RTOS的中斷服務(wù)例程或者延時(shí)任務(wù)一樣的操作,然后使用SVR4進(jìn)程間通信函數msgsnd()將消息放進(jìn)隊列,等待被另一個(gè)本地線(xiàn)程或者另一個(gè)進(jìn)程利用函數msgrcv()來(lái)獲取。這種快速”臟的”處理方法是好的原型,同時(shí)對于建立可發(fā)布型代碼帶來(lái)了巨大的挑戰。首先重要的是需要在用戶(hù)空間掃描中斷。象DOS仿真(DOSEMU)項目提供基于信號的帶SIG(Silly中斷發(fā)生器)中斷I/O,但是用戶(hù)空間的中斷處理過(guò)程非常慢(毫秒量級中斷延時(shí),所替代的基于內核的中斷服務(wù)例程中斷延時(shí)為數十微秒)。更進(jìn)一步講,在用戶(hù)空間的切換調度不能保證用戶(hù)空間的I/O線(xiàn)程100%的及時(shí)執行,即使采用可搶占Linux內核和實(shí)時(shí)調度策略。
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)linux相關(guān)文章:linux教程
評論