下面這幾句話也是在網絡上看到,感覺寫的很通俗易懂供參考:
任務狀態
簡單分為運行態,就緒態,阻塞態。
運行態:萬事俱備,不欠東風(獲得CPU控制權);
就緒態:萬事俱備,只欠東風(缺少CPU控制權);
阻塞態:萬事不俱備(等事件或信號),還欠東風(缺少CPU控制權);
(運行態:老板簽過字、銀行正常上班有錢------可以正常發薪水)
(就緒態:老板沒簽字、銀行正常上班有錢 ----- 還需要等待)
(阻塞態:老板沒簽字、銀行正常上班沒錢 ----- 還需要等待)
每個任務基本上都會游離于這三種狀態。
運行到阻塞,就緒到運行稱為任務切換過程。
從用戶的角度看,UCOSIII的任務一共有5種狀態:
1、休眠態:
任務已經在CPU的flash中了,
但是還不受UCOSIII管理。
2、就緒態:
系統為任務分配了任務控制塊,
并且任務已經在就緒表中登記,
這時這個任務就具有了運行的條件,
但是沒有獲得CPU 的使用權
此時任務的狀態就是就緒態。
3、運行態:
任務獲得CPU的使用權,正在運行。
4、等待態:
正在運行的任務需要等待一段時間,
或者等待某個事件,
這個任務就進入了等待態,
此時系統就會把CPU使用權轉交給別的任務。
5、中斷服務態:
當發送中斷,
當前正在運行的任務會被掛起,
CPU轉而去執行中斷服務函數,此時任務的任務狀態叫做中斷服務態。
白話任務堆棧、任務控制塊、任務函數
在多任務操作系統中創建任務時,都需要指定該任務的堆棧大小,
那么這個堆棧的作用時什么呢?
什么情況下需要用到堆棧,以及大小不夠時會產生什么異常呢?
任務堆棧的作用:
1 任務處于運行狀態:
當任務在運行時,
一般都會調用各式各樣的函數,
而函數的局部變量,參數,返回值是存在于函數棧幀里的,
每個函數都擁有獨立的棧幀,
各個棧幀使用的空間就是任務堆棧的空間。
所以任務堆棧的作用是用于保存函數在運行/調用過程中的參數/局部變量。
(理解堆棧就是個倉庫倉庫里各種各樣的商品也就是各種功能不同函數)
2 任務處于切換
當運行的任務被切換時,需要保護現場(CPU中寄存器的值),
以便于下次恢復數據。
所以任務堆棧的作用是用于保存CPU中寄存器的值。
3
任務(低優先級的)在運行過程中,
隨時可能被切換,
所以CPU中寄存器的值入棧的棧位置是不確定的,
這取決于當前任務的執行情況。
4 堆棧溢出
若堆棧的空間設置太大,會浪費內存資源。
而設置得太小,則會出現堆棧溢出,在沒有MMU功能的操作系統中,
可能會導致系統奔潰。
所以,
需要根據任務的情況設置合適的堆棧大小。
同時,應避免使用遞歸調用函數,
函數中局部變量的分配空間不能太大。
任務堆棧是任務的重要部分,
堆棧是在RAM中按照“先進先出(FIFO)”的原則
組織的一塊連續的存儲空間。
為了滿足任務切換和響應中斷時保存CPU寄存器中的內容及
任務調用其它函數時的需要,
每個任務都應該有自己的堆棧。
一般是這樣定義的
#define START_STK_SIZE 512 //堆棧大小
CPU_STK START_TASK_STK[START_STK_SIZE];//定義一個數組來作為任務堆棧
一般就是這樣定義的,
明白就行不能有壓力。
任務堆棧的大小是多少呢?
CPU_STK為 CPU_INT32U類型,
也就是 unsigned int類型,
為4字節的,
那么任務堆棧
START_TASK_STK的大小就為:512 X 4=2048字節!
任務如何才能切換回上一個任務
并且還能接著從上次被中斷的地方開始運行?
這一點很重要系統是如何來切換任務的?
它是怎么知道某一個任務運行到了那里?
它是怎么來記錄任務運行過程中產生的數據?
這就是任務堆棧初始化的工作
這里是不是和剛學習單片機c51有些類似
org 0x0300
ljmp main
main: mov r0,r1
.....
.....
.....
.....
ajmp main
道理是一樣的
只不過在C 中我們直接定義了幾個大的ROM
每個ROM有是連續的存儲空間
好似這樣的結構
................
. c51 .
. .
. .
. void main() .
. .
. .
. .
................
........................................................
. stm32 . .
. ucos - iii . .
........................................................
. void main() . . . .
. .void main() . . .
. . .void main() . .
. . . . void main() .
........................................................
51 的ROM 空間有過2K 、16K、64K 的
STM32 的空間就大的多了
這樣利用ucos iii 的組織能力就能在其內部分割出來
相同或不相同的 void main(); 來
這其實就是任務堆棧的原型了。
至于在單片機內部是如何分配的就不知道了。
知道堆棧的大概了那么
恢復現場就是重新讀取保存下來的CPU的內部各個寄存器數據。
因此在創建一個新任務時,
必須把系統啟動這個任務時所需的CPU各個寄存器初始值事先存放在任務堆棧中。
這樣當任務獲得CPU使用權時,
就把任務堆棧的內容復制到CPU的各個寄存器,
從而可以任務順利地啟動并運行。
把任務初始數據存放到任務堆棧的工作就叫做任務堆棧的初始化,
UCOSIII 提供了完成堆棧初始化的函數:
OSTaskStkInit()。
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task,
void *p_arg,
CPU_STK *p_stk_base,
CPU_STK *p_stk_limit,
CPU_STK_SIZE stk_size,
OS_OPT opt)
用戶一般不會直接操作堆棧初始化函數,
任務堆棧初始化函數由任務創建函數OSTaskCreate()調用。
不同的CPU對于的寄存器和對堆棧的操作方式不同,
因此在移植UCOSIII的時候需要
用戶根據各自所選的CPU來編寫任務堆棧初始化函數。
前面我們創建了一個任務堆棧,怎么使用這個任務堆棧?
作為任務創建函數OSTaskCreate()的參數,
函數OSTaskCreate()如下:
void OSTaskCreate (OS_TCB *p_tcb,
CPU_CHAR *p_name,
OS_TASK_PTR p_task,
void *p_arg,
OS_PRIO prio,
CPU_STK *p_stk_base, //任務堆棧基地址
CPU_STK_SIZE stk_limit, //任務堆棧棧深
CPU_STK_SIZE stk_size, //任務堆棧大小
OS_MSG_QTY q_size,
OS_TICK time_quanta,
void *p_ext,
OS_OPT opt,
OS_ERR *p_err)
其實看著有些復雜,其實很多參數都是一定的;
學習過后就理解
基礎東西很重要
特別是C 語言的基礎知識和算法
因為基礎的都是實際來處理具體問題的
即便上了系統
系統也只是起到一個調度的作用
平衡各個任務之間的運行
函數OSTaskCreate()中的參數p_stk_base如何確定?
根據堆棧的增長方式,
堆棧有兩種增長方式:
向上增長:堆棧的增長方向從低地址向高地址增長。
向下增長:堆棧的增長方向從高地址向低地址增長
函數OSTaskCreate()中的參數p_stk_base是任務堆棧基地址,
那么如果CPU的堆棧是向上增長的話
那么基地址就&START_TASK_STK[0],
如果CPU堆棧是向下增長的話
基地址就是&START_TASK_STK[START_STK_SIZE-1]
STM32的堆棧是向下增長的!
寫了很多廢話,只是 為了了解任務堆棧
也寫的沒有趣味性了
失敗
|