REX系統之我見

來源:互聯網
上載者:User

http://blog.csdn.net/fyh2003/article/details/6176393

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,我們可以把它們理解成都裡啟動並執行功能模組。每個任務都有自己獨立的堆棧、Runspace、訊號、隊列等,它們之間是獨立的、不互相干擾的,每個任務都實現一個相對獨立的功能。但是每個任務都要求有自己獨立的堆棧,這就要求在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 );

http://blog.csdn.net/fyh2003/article/details/6176393

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,我們可以把它們理解成都裡啟動並執行功能模組。每個任務都有自己獨立的堆棧、Runspace、訊號、隊列等,它們之間是獨立的、不互相干擾的,每個任務都實現一個相對獨立的功能。但是每個任務都要求有自己獨立的堆棧,這就要求在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 );

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.