使用過ucosii的朋友應(yīng)該都會(huì)知道,單片機(jī)+嵌入式實(shí)時(shí)操作系統(tǒng)能夠做到盡可能最大化的利用cpu資源,通過加入實(shí)時(shí)操作系統(tǒng)能夠做出更加強(qiáng)大的產(chǎn)品和應(yīng)用。
不知道使用過ucosii的朋友有沒有去了解過它進(jìn)行任務(wù)調(diào)度的原理和實(shí)現(xiàn)方式呢?
我個(gè)人結(jié)合ucosii的源碼和自己的理解,分享一些有關(guān)ucosii的任務(wù)管理和調(diào)度的實(shí)現(xiàn)。
1、ucos-ii 任務(wù)創(chuàng)建與任務(wù)調(diào)度
1.1、任務(wù)的創(chuàng)建
當(dāng)你調(diào)用 OSTaskCreate( ) 進(jìn)行任務(wù)的創(chuàng)建的時(shí)候,會(huì)初始化任務(wù)的堆棧、保存cpu的寄存器、創(chuàng)建任務(wù)的控制塊(OS_TCB)等的操作;
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */
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); /* Initialize the task's stack */
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);
}
** 注意:ucosii不支持兩個(gè)及以上相同的任務(wù)優(yōu)先級(jí)的任務(wù),ucosiii支持時(shí)間片輪轉(zhuǎn)。**
ucosii 的任務(wù)控制塊是任務(wù)中很重要,它記錄了任務(wù)的信息,包括優(yōu)先級(jí)、延時(shí)時(shí)間、狀態(tài)等信息。控制塊定義如下:
typedef structos_tcb {
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
INT16U OSTCBId; /* Task ID (0..65535) */
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
OS_EVENT **OSTCBEventMultiPtr; /* Pointer to multiple event control blocks */
void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */
OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
INT8U OSTCBStat; /* Task status */
INT8U OSTCBStatPend; /* Task PEND status */
INT8U OSTCBPrio; /* Task priority (0 == highest) */
INT8U OSTCBX; /* Bit position in group corresponding to task priority */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
OS_PRIO OSTCBBitX; /* Bit mask to access bit position in ready table */
OS_PRIO OSTCBBitY; /* Bit mask to access bit position in ready group */
INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */
INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */
INT32U OSTCBCyclesTot; /* Total number of clock cycles the task has been running */
INT32U OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption */
OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */
INT32U OSTCBStkUsed; /* Number of bytes used from the stack */
INT8U *OSTCBTaskName;
INT32U OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
} OS_TCB;
2、任務(wù)調(diào)度實(shí)現(xiàn)
2.1、將任務(wù)優(yōu)先級(jí)進(jìn)行分組
因?yàn)閡cosii最大優(yōu)先級(jí)數(shù)量為64個(gè),所以可以分成8組,每組8個(gè)優(yōu)先級(jí)。
當(dāng)一個(gè)任務(wù)被創(chuàng)建成功之后,它的組號(hào)由優(yōu)先級(jí)的高三位決定(bit5 bit4 bit3),它在組內(nèi)的編號(hào)由優(yōu)先級(jí)的低三位決定(bit2 bit1 bit0),如下:
ptcb->OSTCBY = (INT8U)(prio >> 3u); // 組
ptcb->OSTCBX = (INT8U)(prio & 0x07u); // 組內(nèi)編號(hào)
2.2、任務(wù)就緒表
ucosii對(duì)任務(wù)優(yōu)先級(jí)的調(diào)度管理是通過查詢?nèi)蝿?wù)就緒表進(jìn)行的。任務(wù)就緒表里面保存著當(dāng)前所有任務(wù)的就緒狀態(tài),如下:
OSRdyTbl[8]
說明:
1)它是uint8的數(shù)據(jù)類型。它的長度是8,每一個(gè)元素代表一個(gè)組,
比如 OSRdyTbl[0]代表第0組, OSRdyTbl[1]代表第1組,OSRdyTbl[2]代表第2組……以此類推。
2)每一個(gè)元素中的每一個(gè)位(bit)代表組內(nèi)的任務(wù)的就緒狀態(tài)(1為就緒,0為未就緒)。
說明:
1)當(dāng)優(yōu)先級(jí)為12 的任務(wù)就緒時(shí),那么對(duì)應(yīng)的OSRdyTbl[1]的第4位bit,絕對(duì)等于1;
當(dāng)整個(gè)系統(tǒng)中,當(dāng)只有優(yōu)先級(jí)為12的任務(wù)就緒,其他所有任務(wù)都沒有就緒時(shí),那么OSRdyTbl[1] 絕對(duì)等于0x10。
2)當(dāng)優(yōu)先級(jí)為0和1的任務(wù)就緒時(shí),那么對(duì)應(yīng)的OSRdyTbl[0]的第0位bit以及第1位bit,都絕對(duì)等于1;
當(dāng)整個(gè)系統(tǒng)中,當(dāng)只有優(yōu)先級(jí)為0和1的任務(wù)就緒,其他所有任務(wù)都沒有就緒時(shí),那么OSRdyTbl[0] 絕對(duì)等于0x03。
2.3、任務(wù)釋放CPU使用權(quán)
當(dāng)任務(wù)中調(diào)用 OSTimeDly( ) 時(shí),會(huì)讓任務(wù)進(jìn)入休眠的狀態(tài),交出CPU的執(zhí)行權(quán)給到其他就緒任務(wù)去執(zhí)行,這個(gè)過程就發(fā)生了任務(wù)的切換。
簡單而言就是會(huì)把任務(wù)就緒表 OSRdyTbl 中對(duì)應(yīng)的任務(wù)優(yōu)先級(jí)在組內(nèi)的編號(hào)狀態(tài)改變,從而使任務(wù)自身進(jìn)入休眠狀態(tài)。代碼如下:
if (ticks > 0u) { /* 0 means no delay! */
OS_ENTER_CRITICAL();
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0u) {
OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next task to run! */
}
在上面的代碼中發(fā)現(xiàn)了一個(gè)東西:OSRdyGrp。這個(gè)有什么用呢?
OSRdyGrp:管理任務(wù)就緒組的
OSRdyGrp是INT8U類型的,它每一個(gè)bit代表一個(gè)組,只要這個(gè)組內(nèi)有任何一個(gè)任務(wù)就緒了,那對(duì)應(yīng)的這個(gè)bit就會(huì)被設(shè)置為1,表示這個(gè)組內(nèi)目前有就緒的任務(wù)。否者對(duì)應(yīng)的位為0。
舉個(gè)例子,如下:
1)系統(tǒng)中只有任務(wù)0就緒了,那么OSRdyGrp 便等于 0x01(二進(jìn)制00000001)。
2)系統(tǒng)中有任務(wù)0和任務(wù)63都就緒了,那么OSRdyGrp 便等于 0x81(二進(jìn)制10000001)。
2.4、任務(wù)實(shí)現(xiàn)調(diào)度切換操作
發(fā)生一次任務(wù)調(diào)度是通過 OS_Sched() 進(jìn)行的。源碼如下:
void OS_Sched (void)
{
OS_CPU_SR cpu_sr = 0u;
OS_ENTER_CRITICAL();
if (OSIntNesting == 0u) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0u) { /* ... scheduler is not locked */
OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
這里的過程如下:
(1)先通過 OS_SchedNew() 找到當(dāng)前處于就緒狀態(tài)的最高優(yōu)先級(jí)的任務(wù),如下:
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
(2)然后通過 OS_TASK_SW() 進(jìn)行任務(wù)切換,它的過程如下:
只是一個(gè)宏,它實(shí)際替換的是 OSCtxSw()
#define OS_TASK_SW() OSCtxSw()
2)OSCtxSw()是由匯編實(shí)現(xiàn)的
OSCtxSw
PUSH {R4, R5}
LDR R4, =NVIC_INT_CTRL ;觸發(fā)PendSV異常 (causes context switch)
LDR R5, =NVIC_PENDSVSET
STR R5, [R4]
POP {R4, R5}
BX LR
就這樣,上下文就完成了一次切換。
評(píng)論
查看更多