任務是操作系統處理的首要對象,在多任務運行環境,任務的管理需要考慮多方面的因素。最基本的任務管理功能是任務的創建。任務創建函數分兩種,一種是基本的創建函數OSTaskCreate,另一種是擴展的任務創建函數OSTaskCreateExt。兩個函數都實現了任務的創建,但是OSTaskCreateExt功能更強,帶有很多附加的功能。如果不需要使用附加的功能,OSTaskCreate也是可以勝任的,沒有哪個更好之說,完全根據需要選擇。
OSTaskCreate實現了創建任務最基本的功能,任務在創建之后,就準備好了運行需要的各種數據結構。OSTaskCreate的代碼如下所示:
50.png (55.8 KB, 下載次數: 93)
下載附件
2013-7-19 00:08 上傳
在創建一個任務的時候,需要給任務分配堆棧空間。堆棧是在操作系統的變量聲明中已經分配了空間的,因此要將該空間分配給任務,那么堆棧的地址應該保存在什么地方呢:任務創建后堆棧又應該初始化什么樣子呢?
51.png (25.49 KB, 下載次數: 94)
下載附件
2013-7-19 00:08 上傳
可以看到,堆棧的初始化不是很好理解。因為opt在這里根本沒有使用,因此不需要過多的考慮。這段程序代碼出現在os_cpu.h中,說明它是和CPU密切相關的,當使用其他硬件環境時,需要修改該代碼。實際上,該代碼就是按順序向堆棧中壓入數據,最后返回最新的堆棧棧頂指針。那么在堆棧中都壓入了哪些數據呢?如下圖所示是處理器的寄存器:
52.png (81.83 KB, 下載次數: 101)
下載附件
2013-7-19 00:08 上傳
代碼注釋部分也提到了,上半部分數據在exception時是自動保存的,下班部分需要手動保存。那么哪些數據是自動保存的呢?如下圖所示:
53.png (36.41 KB, 下載次數: 91)
下載附件
2013-7-19 00:08 上傳
以空閑任務的創建為例,在堆棧初始化之后,堆棧如下圖所示:
54.png (17.82 KB, 下載次數: 86)
下載附件
2013-7-19 00:08 上傳
空閑任務的堆棧定義如下所示:
55.png (2.91 KB, 下載次數: 79)
下載附件
2013-7-19 00:08 上傳
設置OS_TASK_IDLE_STK_SIZE這個宏的值是32,即:
56.png (3.14 KB, 下載次數: 72)
下載附件
2013-7-19 00:08 上傳
STM32F407的堆棧是向低地址增長的,所以最開始堆棧頂的位置在最高地址OSTaskIdleStk[31]。所以將&OSTaskIdleStk[31]作為棧頂地址傳遞給堆棧初始化函數OSTaskStkInit。
任務調度函數OS_Sched將進行任務的調度, 即選擇優先級最高的任務來運行。任務的地址、運行環境等內容都保存于堆棧,OS_Sched將根據堆棧的內容進行相關的操作。在操作系統初始化的時候,由于還沒有啟動多任務,因此還不會調用OS_Sched。
從前面的基本代碼描述,可以清晰了解任務初始化的過程。首先,如果配置了對任務參數進行檢查,則檢查任務參數的有效性,尤其是對任務的優先級進行判斷。當任務的優先級在合適的范圍內時,還需要判斷指定優先級的任務是否有已經被創建的,因為優先級必須是唯一的。然后進行任務堆棧的初始化、任務控制塊的初始化,根據系統是否已經啟動多任務,決定是否此時進行一次任務調度。OSTaskCreate的流程如下圖所示:
57.png (32.8 KB, 下載次數: 98)
下載附件
2013-7-19 00:08 上傳
OSTaskCreate實現了創建任務的最基本功能,但是操作系統默認使用的是任務創建函數卻是OSTaskCreateExt,那么OSTaskCreateExt比OSTaskCreate多了哪些功能呢?
OSTaskCreateExt的函數參數就有9個,但是并非想象中的那么復雜。相對OSTaskCreate來說,最主要的是增加了堆棧檢查的功能,該函數的聲明如下所示:
58.png (45.34 KB, 下載次數: 88)
下載附件
2013-7-19 00:10 上傳
59.png (18.39 KB, 下載次數: 119)
下載附件
2013-7-19 00:10 上傳
這個函數是uC/OS-II的內部函數,用于采用擴展功能創建任務。
參數解析如下:
(#_#)task:任務的地址,操作系統根據該地址找到任務,才能讓任務運行。這與OSTaskCreate是完全相同的。
(#_#)p_arg:任務參數,以地址形式傳遞,可以是任何類型的地址。
(#_#)ptos:任務堆棧的棧頂。需要注意的是,如果OS_STK_GROWTH的值設置為1,堆棧是從高地址向低地址方向增長,棧頂應為高地址,否則堆棧是從低地址向高地址方向增長。棧頂在低地址。
(#_#)prio:任務的優先級。在uC/OS-II中,任務的優先級必須唯一。
(#_#)id:任務的識別號ID,范圍為0~65536.
(#_#)pbos:任務堆棧的棧底地址。如果OS_STK_GROWTH設置為1,則堆棧是從高地址向低地址方向增長,pbos應該在低地址端。反之,如果OS_STK_GROWTH設置為0,則堆棧是從低地址向高地址方向增長,pbos應該在高地址端。
(#_#)stk_size:堆棧的大小。stk_size設置為可以壓入堆棧的最大數據量,和堆棧的數據類型無關。如果堆棧的類型OS_STK為32位整型,那么堆棧共有stk_size*4字節。如果堆棧的類型OS_STK為8位的整型,那么堆棧共有stk_size字節。
(#_#)pext:擴展塊的地址。如果支持浮點數計算,用戶內存在上下文切換(任務切換)時需要保存和恢復浮點寄存器的內容,這些內容可以保存在擴展快中。
(#_#)opt:包含任務的附件信息。opt的低8位為uC/OS-II保留,高8位用戶可設置。低8位的每一位的含義與OS_TASK_OPT相同,因此系統定義了4個宏,如下所示:
60.png (11.91 KB, 下載次數: 101)
下載附件
2013-7-19 00:10 上傳
返回值如下:
(#_#)OS_ERR_NONE:本函數成功運行。
(#_#)OS_PRIO_EXIT:如果任務優先級已經存在了,再創建相同優先級的任務明顯是非法的。
(#_#)OS_ERR_PRIO_INVALID:如果優先級參數非法,如大于最高值OS_LOWEST_PRIO。
(#_#)OS_ERR_TASK_CREATE_ISR:如果在中斷服務程序ISR中創建任務。
OSTaskCreateExt與OSTaskCreate的主要區別在于是用堆棧清除函數OS_TaskStkClr來清空堆棧。
我們需要知道OS_TaskStkClr是如何進行堆棧的清空的。如下所示:
61.png (44.08 KB, 下載次數: 74)
下載附件
2013-7-19 00:10 上傳
它的3個參數分別為堆棧的棧底、堆棧的大小及選項opt,可以看到這3個參數都是傳遞給OSTaskCreateExt后直接傳遞給OS_TaskStkClr的。
可見,堆棧清除函數實現的功能就是將整個任務對應的堆棧空間全部清0,這樣做是為了符合以后進行堆棧檢查的需要。如果不需要進行堆棧檢查,當然也不需要進行堆棧的清除操作,這是程序代碼第一行條件判斷語句存在的原因。另外,如果在堆棧清除函數中,發現opt未設置有OS_TASK_OPT_STK_CLR,就不應執行堆棧清除操作。
任務創建函數是非常重要的函數,在操作系統的初始化中就調用了這個函數來創建了空閑任務和統計任務。OSTaskCreateExt的流程如下所示:
62.png (36.17 KB, 下載次數: 102)
下載附件
2013-7-19 00:10 上傳
與OSTaskCreate相比,OSTaskCreateExt比OSTaskCreate增加了堆棧清除的功能,其他并無大的卻別。重復以下兩點:堆棧清除函數只是為堆棧檢查做準備的;操作系統采用的默認的任務創建函數是OS_TaskCreateExt。
任務創建函數執行后,在正常情況下,對應的任務就處于就緒態,因為在創建的過程中, 調用了TCB初始化函數,對就緒表和就緒組進行了操作和處理,標記了任務的就緒狀態,而且也在就緒隊列插入了一個TCB。 |