ucos ii是怎麼實現多任務運行的?

ucos上建立一個任務,格式如上圖,它是一個死循環,但如果我建立了五個任務,並且五個任務裡面沒有延時,就只是像無操作系統那樣寫法,用死循環讓它們一直跑,那這五個任務可以實現並行嗎。

經大神提醒,用並行這個詞是錯的,應該是並發。^_^


RTOS有一個系統時鐘節拍,ucos有API可以自己設置,通常設置為1ms,意味著每1ms調度一次任務,ucos不支持時間片輪番調度(不支持相同的優先順序)。除非等待資源或者你自己調用延時,否則高優先順序的任務就一直佔用CPU。(對於linux會分時復用,ucos不會)

/****二次修改****/

@DarinLi

查了下ucosiii支持時間片調度,很久沒寫mcu程序了


和中斷程序打斷執行的機制類似。


最近有用到ucosiii,跟ucosii還是有點區別,但是本質還是沒有多大變化。一般跑ucos的都是單片機。如果是Soc基本上都跑linux了。這裡我們要說一下基本知識,就是CPU的線程問題,像我電腦的CPU是i7的,四核八線程,所以他可以同時跑個8任務。而單片機只是單核單線程,所以每次只能跑一個任務,如果是裸機,那就是main()函數裡面面的while(1)。那中斷和定時器也是一樣的不是意味著他可以同時執行中斷任務和mian函數的任務,而是觸發中斷之後,暫停main函數中的任務,把當前的任務信息保存起來,然後去執行中斷函數裡面的任務,執行完了再恢復mian函數,從上次中斷的地方繼續執行。如果中斷任務執行的時候,又有其他中斷且是優先順序比當前執行中斷高的,那個這個中斷會暫停,把當前任務狀態保存起來,去執行那個優先順序更高的中斷,執行完成後再執行這個中斷,然後再繼續執行main函數。

所以,有人就想了這樣一個辦法,用定時器計時進行計數,然後把任務的總周期定下來,然後把時間分成好幾份,分配給不同的函數,此時這每一個函數執行不同的功能,相同於執行不同的任務。這種做法應該叫狀態機,不能叫操作系統。匿名四軸飛行器最早期的代碼就是這樣子寫的,不過他全部代碼放在中斷裡面執行,這樣就不用自己寫任務切換函數了,但是這個方法不是最好的方法,會有一些問題,中斷函數應該不能寫過於大的任務。

所以,現在的ucos之類的操作系統都採用了分時復用的方法,就是在不同的時間執行不同的任務函數。這個時鐘由滴答定時器產生,一般為5ms中斷一次,每次中斷,都會去檢查任務就緒表,如果有高優先順序任務就緒了,就去執行他,然後把當前執行的任務保存在任務堆棧中,所以每個任務創建之前,都要先初始化一片內存用於存放當前任務執行的狀態,如下面代碼所示:

#define NRF24L01_STK_SIZE 128
OS_TCB NRF24L01TCB;
CPU_STK NRF24L01_STK[NRF24L01_STK_SIZE];
OSTaskCreate ((OS_TCB *)NRF24L01TCB,
(CPU_CHAR *)"NRF24L01",
(OS_TASK_PTR )NRF24L01_task,
(void *)0,
(OS_PRIO )NRF24L01_TASK_PRIO,
(CPU_STK *)NRF24L01_STK,
(CPU_STK_SIZE )NRF24L01_STK_SIZE/10,
(CPU_STK_SIZE )NRF24L01_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
(OS_ERR *)err);

這就是創建一個任務的函數,比ucosii多了好多參數。題主問這個問題是在奇怪為什麼每個任務都是while(1),那還么去執行其他任務嗎?其實關鍵就在這個滴答定時器的中斷上面,因為只要進了中斷,就跳出當前的while(1),然後我們就可以把當前的程序指針(PC寄存器)指向別的地方。其實每一個函數都存放在不同的地方,只要知道我下一個要執行的任務函數的首地址,那我只要把指針指過去就好了。可以看到上面初始中有一個:

(OS_TASK_PTR )NRF24L01_task,

這個就是任務函數的地址。這個地址會保存在一個數據結構裡面,這個叫什麼我也不記得了,任務控制塊?

所以你只要知道,單片機每次只能執行一個任務,多任務是分時復用來實現的,即ucos採用的方法。每個任務函數都是一個地址指針,要運行哪一個指令,就看PC寄存器指到哪。所以這就是為么要用滴答定時器的中斷,因為我們可以在中斷裡面改變PC寄存器裡面的這個地址,從而去到別的任務中,然後我們恢復那個任務上一次的執行狀態,這就是為什麼要開一個任務堆棧,就是為了存放任務執行的狀態,包括各種變數的值。

這個是ucosiii的任務狀態轉換圖,只要你在while(1)任務函數裡面調用了這些API,都會觸發任務調度,例好OSTimeDlyHMSM()系統級延時函數,到了時間之後又會回到上次執行的地方開始執行。這裡要說一點的就是,如果高優先順序的任務一直不進行任務調度,那麼該任務就會一直佔用CPU,低優先順序任務將一直無法執行。而最低優先順序的任務即使沒有進行任務調度,也會進行任務切換,因為滴答定時器每5ms進行一次中斷,這個時候就會對任務就緒表進行掃描,如果有高優先順序的任務進入了就緒狀態,那麼就會自動進行任務切換。所以後面就會涉及到一些加鎖的行為,為了防止在運行的時候,時序被打斷,下次回來就無法執行的情況。

有錯的地方請指各位知友指正。


瀉藥,但是我真的不會啊


非常巧,我仿照ucos寫了一個操作系統,大概看十分鐘,就能徹底解決你的問題。光聽別人給你做文字解釋,應該很難懂。http://www.zhihu.com/question/25628124/answer/133388181


好像 ucos ii 不支持多核,那麼它只可以並發 concurrent,不能並行 parallel。


如果像你這樣寫是不能實現任務切換的,ucos的任務切換是根據任務的優先順序的,例如一個高級別任務,在等待隊列或是信息量,或是delay的時候,它是掛起的,這時會實現任務的切換,執行其它已經準備好的任務。按題主的意思,有兩種情況:

1.如果你最高級別的任務一直死循環,那麼及時低級別的任務準備就緒,內核也不會執行低基本任務。

2.如果是低級別任務一致死循環,那麼當高級別任務準備就緒以後,內核可以實現任務切換。你看下ucos,系統有個空閑任務,他的級別最低,就是一直死循環。當你的應用程序的任務都掛起時,執行的是該任務。


題主這樣的操作是不可以進行任務切換的。

因為ucos比較簡單,一個優先順序只能有一個任務,是一個搶佔式內核,沒有考慮低優先順序飢餓的問題。任務里如果沒有延時,那麼不支持切換到低優先順序,就只有一個最高優先順序任務在跑。

每個任務必須要有delay或pend. 否則沒有中斷的話就無法進行任務切換。

高優先順序任務必須要有delay或pend,否則低優先順序任務會一直得不到運行。

當年還看過UCOS-II的源碼。

大概就是使用時鐘節拍觸發中斷,執行一小段中斷程序進行調度,或者調用延時觸發調度:保存上下文(PC寄存器,CPU狀態寄存器,堆棧指針寄存器,幾個運算寄存器),選出當前就緒的最高優先順序任務,恢復最高優先順序任務的上下文。

基本上操作系統任務切換原理大致都是這樣的流程。

微處理器有時間中斷,相對於當前CPU的處理,它是一個非同步的過程,每隔一段時間它都會打斷當前CPU執行流程,去執行一段程序,進行搶佔式調度。

調用延時,獲取信號量等操作也會觸發調度。

這個系統使用8個位元組保存任務狀態,每個任務1bit,保存64個任務的狀態,每次時鐘中斷,都會選出當前就緒的最高優先順序任務,並進行切換。


你應該去理解一下中斷的概念。

程序死循環並不意味著CPU的所有運行時間都在這段程序上,中斷可以中止當前程序的運行,轉而讓CPU去運行另一段代碼(中斷服務程序)。中斷退出後,可以繼續之前運行的代碼。ucos的任務調度也是模擬了這個過程,定時器定時中斷(心跳),然後檢查待運行的高優先順序任務,如果有就會跳過去運行了。

所以問題不在於死循環,寫成死循環也是一樣可以運行的。


如陳碩所說,是並發,不是並行。

實現上採用timer tick中斷來完成調度,每次任務調度根據各個任務的優先順序以及剩餘時間片來進行。

所以那五個循環會被調度分時運行。


原理是系統以固定的頻率產生中斷,並在中斷中進行任務切換(如果條件滿足的話)

如果任務的優先順序相同,可以通過使能時間片輪轉調度來實現。

優先順序不同的話,高優先順序的任務會一直佔用CPU,不會發生任務切換。

所以,除了事件觸發類任務,周期進行的任務都需要調用系統延時函數,以釋放CPU。


1.五個任務在創建時都必須被賦予一個各自的優先順序

2.任務會處於不同的狀態,例如掛起,等待,運行等等,單CPU同一時刻只有一個任務處於運行態

3.操作系統會一直檢查(時鐘拍其實我理解就是定時器中斷吧。。)有沒有比正在運行的任務優先順序高的任務進入就緒態,如果有就會進行調度。

4.並發的意思是在一段時間內都每個任務都被觸發過,那麼顯然只要控制好每個任務的狀態和延遲,就能實現所有任務都被觸發一遍


這個例子不太好,最好是加入系統的延時函數。OSTimeDly()或OSTimeDlyHMSM()。

她多任務是中斷實現的,簡單的說,是有一個任務調度函數(OSSched()),程序員要保證正常情況下,該函數每一小段時間(比如1ms)需要被執行一次,她決定接下來運行哪個任務。如果題主需要詳細了解過程,可以參考《嵌入式實時操作系統 μC/OS - II》(Jean J.Labrosse)

比如把ucosII移植到STM32下,一般在Sysick定時器的中斷函數里調用該函數。


曾經看過ucos ii 任務調度的源碼,自己也實現了一個edf任務調度。

隱約記得在中斷中是會進行任務調度演算法,還有就是調用延時之類的系統函數後會再進行一次任務調度。

ucos的任務調度就是直接選擇已就緒的優先順序最高的任務運行,時間複雜度O(1)。

如果是單核cpu,任務沒有延時也不會退出,那麼只有優先順序最高的任務會一直運行,其他任務沒有運行機會。


一共都沒有幾行代碼,寫得也不錯,自己讀讀,讀不懂再來問。


推薦閱讀:

在北美大學取得教職(assistant professor)是怎樣的體驗?
FPGA與嵌入式Linux選哪個?
上拉電阻難道不會分壓嗎?尤其是弱上拉,那麼大的電阻值?
學電子的有沒有一次都沒有燒壞元器件的?
在中航工業618所工作是什麼樣的體驗?

TAG:嵌入式系統 | 電子 | 單片機 | 電子工程師 | ucos |