從零開始學習UCOSII操作系統4--任務管理
1、重講任務
(1)任務可以是一個無限的循環,也可以在一次執行完畢後被刪除。
這裏需要注意的是,任務的代碼並不是真正的刪除了,而是UCOSII不再理會該任務代碼,所以該任務代碼不會再執行。
(2)建立任務,OSTaskCreate()
如果想讓UCOSII管理用戶的任務,必須先建立任務,可以通過將任務的地址(函數名)和其他參數傳遞到這2個函數中來建立任務。
(3)任務可以在多任務調度之前開始建立,也可以在其他的任務中創建需要的任務。但是有一點需要注意的是,在啓動UCOS之前必須至少得建立一個任務。
2、分析創建任務函數
(1)參數分析:
參數1:任務的函數名:其實就是爲了在任務切換的時候跳轉到任務中執行的入口地址。
參數2:傳遞給建立任務的參數,這個參數基本不會用到。
參數3:傳遞給建立任務的堆棧,每個任務都有獨一無二的堆棧。
參數4:傳遞給任務的優先級。
(2)函數內容分析:
當OS_TASK_CREATE_EN宏大於0的時候,
我們纔可以使用創建任務的函數。
如果創建的時候檢測到任務的優先級比最大的優先級(數值上,實際上是最小)還大的話,那麼就直接退出,輸出一個錯誤碼。
我們不允許創建任務是在中斷中進行的,所以我們也會在中斷時創建任務返回一個錯誤碼。
最後就是把剛剛的四個參數賦值到任務當中去,實現任務的創建。
#if OS_TASK_CREATE_EN > 0u
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio)
{
OS_STK *psp;
INT8U err;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (prio > OS_LOWEST_PRIO) { /* 確認優先級在一個合法的範圍內 */
return (OS_ERR_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0u) { /* 確保我們不在中斷中創建任務 */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* 初始化任務的優先級 */
OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = OSTaskStkInit(task, p_arg, ptos, 0u); /* 初始化任務堆棧 */
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);
if (err == OS_ERR_NONE) {
if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */
OS_Sched();
}
} else {
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */
OS_EXIT_CRITICAL();
}
return (err);
}
OS_EXIT_CRITICAL();
return (OS_ERR_PRIO_EXIST);
}
#endif
3、再談任務堆棧
任務的堆棧可以使用靜態的堆棧生成,也可以使用動態的堆棧生成。
(1)靜態堆棧:
static OS_STK MyTaskStack[stack_size];
(2)動態堆棧:
OS_STK *pstk;
pstk = (OS_STK *)malloc(stack_size);
if(pstk != (OS_STK *)0)
{
//確保malloc能夠得到足夠的內存空間
}
(3)UCOSII支持的堆棧可以是遞減的,也可以是遞增的。
在調用函數OS_TaskCreate(),必須知道堆棧是遞減的,還是遞增的。
因爲必須把堆棧的棧頂地址傳遞給上面的兩個函數。
PS:這裏面就有OS_CPU.h文件中的OS_STK_GROWTH爲0,需要將堆棧的最低地址傳遞給任務創建的函數。
這個是堆棧從下往上增長的:
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[0], prio);
這個是堆棧從上往下增長的:
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);
4、刪除任務,OSTaskDel()
(1)有時候我們需要刪除任務,就是說任務返回到休眠狀態,並不是說任務代碼被刪除了,而是僅僅從就緒隊列中刪除了而已。
參數1:prio :也就是該任務的優先級,
當我們支持多任務相同優先級的時候,必須指明任務堆棧,或者任務名,才能刪除。
(2)實現這個函數的關鍵步驟:也就是我做中文註釋的地方:
4.2.1、把任務從就緒表中移除,也就是不讓該任務處於就緒狀態中。
4.2.2、假如任務需要事件控制塊,消息隊列,郵箱等,那麼我們就需要在刪除任務之前將他所在的鏈表中移除。
4.2.3、把任務的各種資源釋放掉。
#if OS_TASK_DEL_EN > 0u
INT8U OSTaskDel (INT8U prio)
{
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
OS_FLAG_NODE *pnode;
#endif
OS_TCB *ptcb;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
if (OSIntNesting > 0u) { /* See if trying to delete from ISR */
return (OS_ERR_TASK_DEL_ISR);
}
if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */
return (OS_ERR_TASK_DEL_IDLE);
}
#if OS_ARG_CHK_EN > 0u
if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */
if (prio != OS_PRIO_SELF) {
return (OS_ERR_PRIO_INVALID);
}
}
#endif
/*$PAGE*/
OS_ENTER_CRITICAL();
if (prio == OS_PRIO_SELF) { /* See if requesting to delete self */
prio = OSTCBCur->OSTCBPrio; /* Set priority to delete to current */
}
ptcb = OSTCBPrioTbl[prio];
if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NOT_EXIST);
}
if (ptcb == OS_TCB_RESERVED) { /* Must not be assigned to Mutex */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_DEL);
}
OSRdyTbl[ptcb->OSTCBY] &= (OS_PRIO)~ptcb->OSTCBBitX;
if (OSRdyTbl[ptcb->OSTCBY] == 0u) { /* 把該任務從就緒表中刪除,也就是置0處理 */
OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
#if (OS_EVENT_EN)
if (ptcb->OSTCBEventPtr != (OS_EVENT *)0) {
OS_EventTaskRemove(ptcb, ptcb->OSTCBEventPtr); /* 把該任務從事件控制塊的列表中移除 */
}
#if (OS_EVENT_MULTI_EN > 0u)
if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from any events' wait lists*/
OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr);
}
#endif
#endif
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
pnode = ptcb->OSTCBFlagNode;
if (pnode != (OS_FLAG_NODE *)0) { /* If task is waiting on event flag */
OS_FlagUnlink(pnode); /* 從事件標誌組中移除 */
}
#endif
ptcb->OSTCBDly = 0u; /* 禁止時間等待 */
ptcb->OSTCBStat = OS_STAT_RDY; /* Prevent task from being resumed */
ptcb->OSTCBStatPend = OS_STAT_PEND_OK;
if (OSLockNesting < 255u) { /* Make sure we don't context switch */
OSLockNesting++;
}
OS_EXIT_CRITICAL(); /* Enabling INT. ignores next instruc. */
OS_Dummy(); /* ... Dummy ensures that INTs will be */
OS_ENTER_CRITICAL(); /* ... disabled HERE! */
if (OSLockNesting > 0u) { /* Remove context switch lock */
OSLockNesting--;
}
OSTaskDelHook(ptcb); /* Call user defined hook */
OSTaskCtr--; /* One less task being managed */
OSTCBPrioTbl[prio] = (OS_TCB *)0; /* Clear old priority entry */
if (ptcb->OSTCBPrev == (OS_TCB *)0) { /* Remove from TCB chain */
ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0;
OSTCBList = ptcb->OSTCBNext;
} else {
ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext;
ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev;
}
ptcb->OSTCBNext = OSTCBFreeList; /* Return TCB to free TCB list */
OSTCBFreeList = ptcb;
#if OS_TASK_NAME_EN > 0u
ptcb->OSTCBTaskName = (INT8U *)(void *)"?";
#endif
OS_EXIT_CRITICAL();
if (OSRunning == OS_TRUE) {
OS_Sched(); /* Find new highest priority task */
}
return (OS_ERR_NONE);
}
#endif
5、掛起任務,OS_TaskSuspend()和恢復掛起任務OSTaskResume()
(1)這個函數是必須進行說明的一個函數,因爲他涉及到任務的狀態機。
(2)這個實現掛起的函數主要是刪除就緒表中的位圖的相應優先級的那個位進行置0的操作。
(3)然後將任務的相應的狀態進行賦值爲掛起的狀態。
(4)最後在最後要進行任務的調度的操作,如果當前是這個任務在進行的話,要切換到別的任務中繼續去運行。
INT8U OSTaskSuspend (INT8U prio)
{
BOOLEAN self;
OS_TCB *ptcb;
if (prio == OS_IDLE_PRIO) { (1)
return (OS_TASK_SUSPEND_IDLE);
}
if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { (2)
return (OS_PRIO_INVALID);
}
OS_ENTER_CRITICAL();
if (prio == OS_PRIO_SELF) { (3)
prio = OSTCBCur->OSTCBPrio;
self = TRUE;
} else if (prio == OSTCBCur->OSTCBPrio) { (4)
self = TRUE;
} else {
self = FALSE;
}
if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { (5)
OS_EXIT_CRITICAL();
return (OS_TASK_SUSPEND_PRIO);
} else {
if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) { (6)//就緒表中的相應的位置0操作
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
ptcb->OSTCBStat |= OS_STAT_SUSPEND; (7) 把TCB的狀態也設置爲掛起的狀態標誌位
OS_EXIT_CRITICAL();
if (self == TRUE) { (8)
OSSched();
}
return (OS_NO_ERR);
}
}
OSTaskResume()恢復任務的源碼也是差不多的,就是一個是對就緒表上面的內容進行置0的操作,一個是對就緒表的內容進行置1的操作。