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