REX系統了解1
REX是高通開發出來的一個操作系統,起初它是為了在Inter 80186處理器上應用而開發的,到後來才轉變成應用在ARM這種微處理器上。他歷經了很多版本,代碼也越來越多,功能也越來越完善。REX只用不到5k的ROM存儲空間,從前REX系統匯編代碼和C代碼加起來不過一千多行,不過現在已經超過一萬五千行了。在功能提升的背後,不但要有高性能硬件的支持,同時要求系統的設計上也要更合理。
作為一個實時的嵌入式操作系統,REX有幾個比較顯著的特點:簡單、高效、搶占式、多任務、實時。
簡單,我們大家都能理解,嵌入式操作系統都有這樣的共同的特點,它不像大型的操作系統一樣提供各種各樣的功能,它只須按照手機的基本需要來提供一些相應的功能即可,而且作為一個嵌入式操作系統,必須要求它有很小的功耗,這樣才能手機在形狀的大小上有合理的控制,也適合手機這種以電池供電的設備的需求。高效的,實時系統不是以每秒鐘進行多少次運算來評價效率的,而是以對一個事件的響應時間和多久做完來評價的。REX是一個Real Time的系統,它要求對事件的反映速度要快,不能說用戶按了一個key,系統等了幾分鐘都沒有相應。REX系統不但響應的快,對事件的處理也快。REX系統是多任務的,在我們的感覺上,REX可以同時執行多個任務,這樣也體現了高效的特點,但事實上它和其他的操作系統一樣,也是以CPU在不同任務中的迅速輪轉來實現形式上的多任務。這樣就出現了一個問題:多任務在CPU之間如何輪轉?這樣就用到了REX的另一個特點:搶占式。REX是搶占式的,當一個最高優先級的任務一旦READY,它總能得到CPU的控制權,也就是說它可以搶占正在運行中的任務的CPU。以前的REX系統中每一個任務都有不同的優先級,總是能明確的比較出處於就緒態的任務中哪一個會獲得CPU,但是到後來的REX版本中,允許不同的任務有相同的優先級,這就要求有另外一種調度方式,也就是在相同優先級的任務之間以時間片輪轉的方式來調度。
上文中我們在討論多任務和任務之間的調度,那麽何為任務呢?在嵌入式實時操作系統中,任務就相當於線程。REX中有很多的TASK,我們可以把它們理解成都裏運行的功能模塊。每個任務都有自己獨立的堆棧、運行空間、信號、隊列等,它們之間是獨立的、不互相幹擾的,每個任務都實現一個相對獨立的功能。但是每個任務都要求有自己獨立的堆棧,這就要求在REX中不能有太多的任務,曾經Web Browser因為占用資源過多而被作為一個任務在REX中運行的,但是後來又把它放入BREW之中。我們要註意,不到萬不得已不能新加任務。REX操作系統首先會建立一個idle task和main task,然後用main task來控制其他任務的創建。Main task也就是mc_task,在它被創建後它會首先創建一些非常重要服務如時鐘、數據服務,數據庫服務、Encoder Driver、RF Driver等等,之後才是定義和創建一些任務,首先它將sleep這個任務定義好,然後就是Dog Task,之後再是其他的任務,對於這些任務的內存拷貝、定義、開始服務的順序我一直很迷惑,考慮到REX嚴格而周密的優先級制度,那麽這些任務的貌似混亂的不同的定義順序肯定也有其道理,雖然我至今尚迷惑於此,不過這樣的確實現了mc_task的靜態創建與mc_task對其他任務的動態創建。REX是通過rex_def_task()這個函數來定義任務的,這個函數先生成一個文間結構到stack,然後建立一個TCB跟這個任務相對應,然後再把task放到task list裏。每個task都有自己的TCB,也就是task control block任務控制塊,TCB裏包含了該任務的堆棧地址,任務入口,任務優先級(priority),任務信號等與TASK相關的信息,REX允許用戶直接操作TCB結構。在這些任務被定義和初始化後並不是立即運行的,而是根據優先級的不同而先後執行的,最高優先級的任務在其就緒之後開始運行,因為優先級是根據軟硬件的邏輯關系及任務本身的重要性和運行頻率來合理的確定下來的,所以這種搶占式的方式在兼顧公平的同時也體現了高效的特點。在程序上這些是利用best task和current task的概念來實現的:
rex_set_best_task( REX_TASK_LIST_FRONT() );
這也就是一個Scheduler的概念,也就是調度。它在REX中是被作為一個function來實現的:rex_sched()。它不能被APP直接調用,在調用rex_sched()前必須設置全局變量:rex_best_task,它的基本算法可以用下面的一段偽代碼表示:
當前任務壓棧保存狀態,等待再次彈出。
任務之間是需要相互通信的,而且任務之間的通信要比APP之間的通信難的多,這也是前文提到的Web Browser從任務變為APP的原因。Task之間的通信是依靠信號量的,也是通過信號量的設置清除和等待促成了任務之間的切換。
典型的,當mc_task創建一個任務的時候,它首先define一個task,初始化這個task需要的數據後mc等待,然後去執行處於ready態的task中優先級最高的,等待該task初始化完畢後準備開始了,再返回給mc一個回饋信號,註意這裏的回饋信號是無所謂哪個信號發出了,也就是有一個task準備就緒了就要返回mc,之後task死循環等待信號,這個信號可以是個cmd信號,可以是個timer信號,也可以是一個start信號。Task在收到該信號後,根據信號的不同來處理這些信號。這樣就可一通過不同的信號來控制task的運行了。
在rex中,這些信號是被保存在task的TCB中的,信號量就是一個標誌位,每一個signal是一個1bit的二進制數,在32位處理器ARM中也就是最多有32個信號量。每個信號量代表不同的意思。任務可以自定義信號量,可以用rex_wait()這個函數來等待某個信號,同時把自己掛起,task也只有通過這種方式掛起,其他task或者中斷可以通過rex_get_sigs(), rex_set_sigs(), rex_clr_sigs(), 來對某一個task的信號操作,當Task A set一個信號給Task B的時候,該任務也會存儲一些數據在特定的buffer中,set一個信號會引起任務調度,Task B接收到這個信號從而處於就緒態,一旦Task B的優先級足夠高而運行後,Task B就可以從buffer中讀取數據,這樣就實現了任務之間的通信。信號被存在任務自己的TCB中,從而實現了信號傳遞的目的性和安全性。
REX也提供了定時器的功能。Timer有兩種,一種是clock timer,一種是任務自定義的timer。Clk_timer到時後回調用call back函數,會立即執行想要的操作,而自定義的timer不會這樣,它只會置一個信號,執行不執行還要看task的優先級。Timer的自定義可通過rex_set_timer來實現,對timer的操作還有:rex_get_timer, rex_resume_timer, rex_cls_timer, rex_pause_timer, rex_timed_wait. 系統每一個時鐘滴答都會去檢查這個timer有沒有到時,REX維持了一個特別的list來存儲timer,timer一旦到期,REX就從list中將timer移除,但並不是刪掉,只是REX不知道這個timer的存在了。Timer只能被task創建和所有。特別要註意的是:不能在活躍的timer上調用rex_set_timer。程序的循環定時檢測就因為timer的存在,timer實現了操作系統對離散時間上的事件的及時響應。
一個timer到了,或者是一個其它信號的設置都可能引起任務的調度,任務的調度中就不得不提到中斷的概念。中斷顧名思義,就是打斷當前的操作,來進行其它操作,操作系統每隔一段時間就要執行一次中斷來檢測當前出於就緒態的task中優先級最高的一個是不是當前任務,或者是任務自發的產生中斷。也就是調用rex_wait。既然中斷會破壞當前任務的運行,但一段代碼不能被打斷的時候就要執行關中斷這個操作,這段代碼就叫做臨界段代碼,當執行完這段代碼後,也要立即開中斷。假如Task a執行關中斷後自願wait了,那麽關中斷會被存儲,而不會影響下一個執行的任務,當Task A再次運行的時候,關中斷的狀態又被恢復。關於中斷有幾點要註意的問題:1.在中斷中的代碼不要操作的太多;2.盡量不要在中斷中使用循環,或者等待;3.在中斷的情況下不要改動全局變量,這樣可能造成被掛起的任務執行錯誤;4. clk_tick_isr也產生中斷。
在操作系統中一定要考慮互斥和共享資源的問題。如果兩個人都要過一條河,但是河上只有一條獨木橋,如果兩個人都同時占用這個橋那兩個人都沒法過河,這就涉及到了互斥的概念。REX提供了Critical section and Mutual exclusion的概念,特別的我們註意到的就是臨界代碼的關中斷,來實現互斥。其實在任務之間的資源互斥還可以用信號量的方法來解決。這樣就出現了一個問題:假如有三個任務,它們的優先級排列順序如下:Task A>Task B>Task C,Task C先運行,它占用著Task A的資源,這時候因為Task A得不到資源而一直處於suspend狀態,而在Task C運行的過程中,Task B就緒了,系統就會在中斷的時候掛起Task C而去運行Task B,這樣就出現了優先級反轉的問題。解決這種問題的辦法就是動態改變優先級,也就是把Task C的優先級提升。REX支持優先級的動態改變,但是這是需要深思熟慮後才能做的,因為優先級都是經過嚴格定義的,一旦改變可能會引起不可預料的後果。優先級的動態定義是通過優先級繼承來實現的也就是Task C繼承了Task A的優先級。
REX有較強的存儲區保護功能,它把一些重要的信息存儲在了NV中。在任務的執行中從NV中讀取信息來供任務使用。嵌入式操作系統所處的硬件上一定很小,REX把靜態變量都存儲在一個區,然後是各個任務所需要的空間,之後是動態變量的存儲區,存儲區的大小應該是實現被告知程序員的,這樣程序員就可以合理的安排所用空間的大小了。一般一個任務的堆棧大小也是確定的,所以在任務中不能使用較大的數組或結構體,因為這樣會撐爆該任務所在的棧。
if(rex_best_task == rex_curr_task)
return ;
else if(the system is currently servicing interrupts)
{
rex_curr_task = rex_best_task;
}
rex_curr_task = rex_best_task;
rex_start_task( rex_best_task );
REX系統了解1