首先從main函數開始,下面是uC/OS-II main函數的大致流程:
main(){
OSInit();
TaskCreate(...);
OSStart();
}
首先是調用OSInit進行初始化,然後使用TaskCreate創建幾個進程/Task,最後調用OSStart,操作系統就開始運行了。
main(){
OSInit();
TaskCreate(...);
OSStart();
}
首先是調用OSInit進行初始化,然後使用TaskCreate創建幾個進程/Task,最後調用OSStart,操作系統就開始運行了。
OSInit
最先看看OSInit完成哪些初始化:
void OSInit (void)
{
#if OS_VERSION >= 204
OSInitHookBegin(); /* Call port specific initialization code */
#endif
void OSInit (void)
{
#if OS_VERSION >= 204
OSInitHookBegin(); /* Call port specific initialization code */
#endif
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_QInit(); /* Initialize the message queue structures */
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_VERSION >= 204
OSInitHookEnd(); /* Call port specific init. code */
#endif
OSInitHookEnd(); /* Call port specific init. code */
#endif
#if OS_VERSION >= 270 && OS_DEBUG_EN > 0
OSDebugInit();
#endif
}
OSDebugInit();
#endif
}
OS_InitMisc()完成的是一些其其他他的變量的初始化:
OSIntNesting = 0; /* Clear the interrupt nesting counter */
OSLockNesting = 0; /* Clear the scheduling lock counter */
OSLockNesting = 0; /* Clear the scheduling lock counter */
OSTaskCtr = 0; /* Clear the number of tasks */
OSRunning = FALSE; /* Indicate that multitasking not started */
OSCtxSwCtr = 0; /* Clear the context switch counter */
OSIdleCtr = 0L; /* Clear the 32-bit idle counter */
OSCtxSwCtr = 0; /* Clear the context switch counter */
OSIdleCtr = 0L; /* Clear the 32-bit idle counter */
其中包括:中斷嵌套標誌OSIntNesting,調度鎖定標誌OSLockNesting,OS標誌OSRunning等。OSRunning在這裏設置爲FALSE,在後面OSStartHighRdy中會被設置爲TRUE表示OS開始工作。
OS_InitRdyList()初始化就緒Task列表:
static void OS_InitRdyList (void)
{
INT8U i;
INT8U *prdytbl;
static void OS_InitRdyList (void)
{
INT8U i;
INT8U *prdytbl;
OSRdyGrp = 0x00; /* Clear the ready list */
prdytbl = &OSRdyTbl[0];
for (i = 0; i < OS_RDY_TBL_SIZE; i++) {
*prdytbl++ = 0x00;
}
OSPrioCur = 0;
OSPrioHighRdy = 0;
OSPrioHighRdy = 0;
OSTCBHighRdy = (OS_TCB *)0;
OSTCBCur = (OS_TCB *)0;
}
首先將OSRdyTbl[]數組中全部初始化0,同時將OSPrioCur/OSTCBCur初始化爲0,OSPrioHighRdy/OSTCBHighRdy也初始化爲0,這幾個變量將在第一個OSSchedule中被賦予正確的值。
OSTCBCur = (OS_TCB *)0;
}
首先將OSRdyTbl[]數組中全部初始化0,同時將OSPrioCur/OSTCBCur初始化爲0,OSPrioHighRdy/OSTCBHighRdy也初始化爲0,這幾個變量將在第一個OSSchedule中被賦予正確的值。
OS_InitTCBList()這個函數看名稱我們就知道是初始化TCB列表。
static void OS_InitTCBList (void)
{
INT8U i;
OS_TCB *ptcb1;
OS_TCB *ptcb2;
static void OS_InitTCBList (void)
{
INT8U i;
OS_TCB *ptcb1;
OS_TCB *ptcb2;
OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); /* Clear all the TCBs */
OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */
ptcb1 = &OSTCBTbl[0];
ptcb2 = &OSTCBTbl[1];
for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { /* Init. list of free TCBs */
ptcb1->OSTCBNext = ptcb2;
#if OS_TASK_NAME_SIZE > 1
ptcb1->OSTCBTaskName[0] = '?'; /* Unknown name */
ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;
#endif
ptcb1++;
ptcb2++;
}
ptcb1->OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */
#if OS_TASK_NAME_SIZE > 1
ptcb1->OSTCBTaskName[0] = '?'; /* Unknown name */
ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;
#endif
OSTCBList = (OS_TCB *)0; /* TCB lists initializations */
OSTCBFreeList = &OSTCBTbl[0];
}
這裏完成的工作很簡單,首先把整個數組使用OSTCBNext指針連接成鏈表鏈起來,然後將OSTCBList初始化爲0,也就是還沒有TCB,因爲還沒有Task產生,OSTCBFreeList指向OSTCBTbl[]數組的第一個表示所有TCB都處於Free狀態。
OS_InitEventList()初始化Event列表。
static void OS_InitEventList (void)
{
#if OS_EVENT_EN && (OS_MAX_EVENTS > 0)
#if (OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
{
#if OS_EVENT_EN && (OS_MAX_EVENTS > 0)
#if (OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT *pevent1;
OS_EVENT *pevent2;
OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table */
pevent1 = &OSEventTbl[0];
pevent2 = &OSEventTbl[1];
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /* Init. list of free EVENT control blocks */
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = pevent2;
#if OS_EVENT_NAME_SIZE > 1
pevent1->OSEventName[0] = '?'; /* Unknown name */
pevent1->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent1++;
pevent2++;
}
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_SIZE > 1
pevent1->OSEventName[0] = '?';
pevent1->OSEventName[1] = OS_ASCII_NUL;
#endif
OSEventFreeList = &OSEventTbl[0];
#else
OSEventFreeList = &OSEventTbl[0]; /* Only have ONE event control block */
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_SIZE > 1
OSEventFreeList->OSEventName[0] = '?'; /* Unknown name */
OSEventFreeList->OSEventName[1] = OS_ASCII_NUL;
#endif
#endif
#endif
}
同樣將EventTbl[]數組中的OSEventType都初始化爲OS_EVENT_TYPE_UNUSED。
OS_InitTaskIdle(),中間我們跳過其他的如Mem等的初始化,看看Idle Task的初始化。
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Top-Of-Stack */
OS_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk[0], /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Top-Of-Stack */
OS_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk[0], /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */
其實Idle Task的初始化很簡單就是調用OSTaskCrete系列的函數創建一個Task, OSTaskCreate我們後面再做進一步分析。
初始化State Task也是類似調用OSTaskCreate系列函數創建Stat Task。這裏只是創建了該Task的各個結構還沒有真正運行該Task,直到OSStart中才依據優先級調度運行。
初始化State Task也是類似調用OSTaskCreate系列函數創建Stat Task。這裏只是創建了該Task的各個結構還沒有真正運行該Task,直到OSStart中才依據優先級調度運行。
OK,到這裏OSInit算高一個段落了,我們接着回到main往下看。
OSTaskCreate
OSTaskCreate負責創建Task所需的數據結構,該函數原形如下所示:
INT8U OSTaskCreate (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio)
INT8U OSTaskCreate (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio)
其中task是一個函數指針,指向該Task所開始的函數,當這個Task第一次被調度運行時將會從task處開始運行。
p_arg是傳給task的參數指針;
ptos是堆棧指針,指向棧頂(堆棧從上往下)或棧底(堆棧從下往上);
prio是進程的優先級,uC/OS-II共支持最大64個優先級,其中最低的兩個優先級給Idle和Stat進程,並且各個Task的優先級必須不同。
p_arg是傳給task的參數指針;
ptos是堆棧指針,指向棧頂(堆棧從上往下)或棧底(堆棧從下往上);
prio是進程的優先級,uC/OS-II共支持最大64個優先級,其中最低的兩個優先級給Idle和Stat進程,並且各個Task的優先級必須不同。
接下來,我們看看這個函數的執行流程:
#if OS_ARG_CHK_EN > 0
if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
if (err == OS_NO_ERR) {
if (OSRunning == 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_PRIO_EXIST);
#if OS_ARG_CHK_EN > 0
if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */
return (OS_PRIO_INVALID);
}
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Make sure we don't create the task from within an ISR */
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_CREATE_ISR);
}
if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */
OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the priority to prevent others from doing ... */
/* ... the same thing until task is created. */
OS_EXIT_CRITICAL();
psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0); /* Initialize the task's stack */
err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);
if (err == OS_NO_ERR) {
if (OSRunning == 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_PRIO_EXIST);
OS_LOWEST_PRIO在ucos-ii.h中被定義爲63,表示Task的優先級從0到63,共64級。首先判斷prio是否超過最低優先級,如果是,則返回OS_PRIO_INVALID錯誤。
然後調用OS_ENTER_CRITICAL(),進入臨界段,在臨界段中的代碼執行不允許被中斷。這個宏是用戶自定義的,一般是進行關中斷操作,例如在x86中的CLI等。這個宏和OS_EXIT_CRITICAL()相對應,這個宏表示離開臨界段。
然後調用OS_ENTER_CRITICAL(),進入臨界段,在臨界段中的代碼執行不允許被中斷。這個宏是用戶自定義的,一般是進行關中斷操作,例如在x86中的CLI等。這個宏和OS_EXIT_CRITICAL()相對應,這個宏表示離開臨界段。
OSTaskCreate不允許在中斷中調用,因此會判斷OSIntNesting是否大於0,如果大於0,表示正在中斷嵌套,返回OS_ERR_TASK_CREATE_ISR錯誤。
接着判斷該prio是否已經有Task存在,由於uC/OS-II只支持每一個優先級一個Task,因此如果該prio已經有進程存在,OSTaskCreate會返回OS_PRIO_EXIST錯誤。
相反,如果該prio先前沒有Task存在,則將OSTCBPrioTbl[prio]置1,表示該prio已被佔用,然後調用OSTaskStkInit初始化堆棧,調用OS_TCBInit初始化TCB,如果OSRunning爲TRUE表示OS正在運行,則調用OS_Sched進行進程調度;否則返回。
相反,如果該prio先前沒有Task存在,則將OSTCBPrioTbl[prio]置1,表示該prio已被佔用,然後調用OSTaskStkInit初始化堆棧,調用OS_TCBInit初始化TCB,如果OSRunning爲TRUE表示OS正在運行,則調用OS_Sched進行進程調度;否則返回。
下面來看看OSTaskStkInit和OS_TCBInit這兩個函數。
OSTaskStkInit是一個用戶自定義的函數,因爲uC/OS-II在設計時無法知道當前處理器在進行進程調度時需要保存那些信息,OSTaskStkInit就是初始化堆棧,讓Task看起來就好像剛剛進入中斷並保存好寄存器的值一樣,當OS_Sched調度到該Task時,只需切換到該堆棧中,將寄存器值Pop出來,然後執行一箇中斷返回指令IRET即可。
OSTaskStkInit的原型如下:
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)
和OSTaskCreate類似,task是進程入口地址,pdata是參數地址,ptos是堆棧指針,而opt只是作爲一個預留的參數Option而保留。返回的是調整以後的堆棧指針。
在OSTaskStkInit中,一般是將pdata入棧,flag入棧,task入棧,然後將各寄存器依次入棧。
在OSTaskStkInit中,一般是將pdata入棧,flag入棧,task入棧,然後將各寄存器依次入棧。
OS_TCBInit初始化TCB數據結構,下面只提取主要部分來看:
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)
{
OS_TCB *ptcb;
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */
if (ptcb != (OS_TCB *)0) {
OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */
OS_EXIT_CRITICAL();
ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */
ptcb->OSTCBPrio = prio; /* Load task priority into TCB */
ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */
ptcb->OSTCBPendTO = FALSE; /* Clear the Pend timeout flag */
ptcb->OSTCBDly = 0; /* Task is not delayed */
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)
{
OS_TCB *ptcb;
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */
if (ptcb != (OS_TCB *)0) {
OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */
OS_EXIT_CRITICAL();
ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */
ptcb->OSTCBPrio = prio; /* Load task priority into TCB */
ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */
ptcb->OSTCBPendTO = FALSE; /* Clear the Pend timeout flag */
ptcb->OSTCBDly = 0; /* Task is not delayed */
#if OS_TASK_CREATE_EXT_EN > 0
ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */
ptcb->OSTCBStkSize = stk_size; /* Store stack size */
ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
ptcb->OSTCBOpt = opt; /* Store task options */
ptcb->OSTCBId = id; /* Store task ID */
#else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */
ptcb->OSTCBStkSize = stk_size; /* Store stack size */
ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
ptcb->OSTCBOpt = opt; /* Store task options */
ptcb->OSTCBId = id; /* Store task ID */
#else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0
ptcb->OSTCBDelReq = OS_NO_ERR;
#endif
ptcb->OSTCBDelReq = OS_NO_ERR;
#endif
ptcb->OSTCBY = (INT8U)(prio >> 3); /* Pre-compute X, Y, BitX and BitY */
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
#if OS_EVENT_EN
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
#endif
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
#endif
OSTaskCreateHook(ptcb); /* Call user defined hook */
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; /* Increment the #tasks counter */
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; /* Increment the #tasks counter */
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
OS_EXIT_CRITICAL();
return (OS_NO_MORE_TCB);
}
首先調用OS_ENTER_CRITICAL進入臨界段,首先從OSTCBFreeList中拿出一個TCB,如果OSTCBFreeList爲空,則返回OS_NO_MORE_TCB錯誤。
然後調用OS_EXIT_CRITICAL離開臨界段,接着對該TCB進行初始化:
將OSTCBStkPtr初始化爲該Task當前堆棧指針;
OSTCBPrio設置爲該Task的prio;
OSTCBStat設置爲OS_STAT_RDY,表示就緒狀態;
OSTCBDly設置爲0,當該Task調用OSTimeDly時會初始化這個變量爲Delay的時鐘數,然後Task轉入OS_STAT_狀態。這個變量在OSTimeTick中檢查,如果大於0表示還需要進行Delay,則進行減1;如果等於零表示無須進行Delay,可以馬上運行,轉入OS_STAT_RDY狀態。
OSTCBBitY和OSTCBBitX的作用我們在等會專門來討論。
然後調用OS_EXIT_CRITICAL離開臨界段,接着對該TCB進行初始化:
將OSTCBStkPtr初始化爲該Task當前堆棧指針;
OSTCBPrio設置爲該Task的prio;
OSTCBStat設置爲OS_STAT_RDY,表示就緒狀態;
OSTCBDly設置爲0,當該Task調用OSTimeDly時會初始化這個變量爲Delay的時鐘數,然後Task轉入OS_STAT_狀態。這個變量在OSTimeTick中檢查,如果大於0表示還需要進行Delay,則進行減1;如果等於零表示無須進行Delay,可以馬上運行,轉入OS_STAT_RDY狀態。
OSTCBBitY和OSTCBBitX的作用我們在等會專門來討論。
緊接着就要將該TCB插入OSTCBList列表中,先調用OS_ENTER_CRITICAL進入臨界段,將該TCB插入到OSTCBList成爲第一個節點,然後調整OSRdyGrp和OSRdyTbl,(這兩個變量一會和OSTCBBitX/OSTCBBitY一起討論),最後將OSTaskCtr計數器加一,調用OS_EXIT_CRITICAL退出臨界段。
OSMapTbl和OSUnMapTbl
剛纔我們看到TCB數據結構中的OSTCBBitX/OSTCBBitY以及OSRdyGrp/OSRdyTbl的使用,這裏專門來討論討論這幾個變量的用法。
uC/OS-II將64個優先級的進程分爲8組,每組8個。剛好可以使用8個INT8U的數據進行表示,於是這就是OSRdyGrp和OSRdyTbl的由來,OSRdyGrp表示組別,從0到7,從前面我們可以看到OSRdyGrp和OSRdyTbl是這麼被賦值的:
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
也就是OSTCBBitY保存的是組別,OSTCBBitX保存的是組內的偏移。而這兩個變量是這麼被初始化的:
ptcb->OSTCBY = (INT8U)(prio >> 3);
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
由於prio不會大於64,prio爲6位值,因此OSTCBY爲prio高3位,不會大於8,OSTCBX爲prio低3位。
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
也就是OSTCBBitY保存的是組別,OSTCBBitX保存的是組內的偏移。而這兩個變量是這麼被初始化的:
ptcb->OSTCBY = (INT8U)(prio >> 3);
ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
ptcb->OSTCBX = (INT8U)(prio & 0x07);
ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
由於prio不會大於64,prio爲6位值,因此OSTCBY爲prio高3位,不會大於8,OSTCBX爲prio低3位。
這裏就涉及到OSMapTbl數組和OSUnMapTbl數組的用法了。我們先看看OSMapTbl和OSUnMapTbl的定義:
INT8U const OSMapTbl[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
INT8U const OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
OSMapTbl分別是一個INT8U的八個位,而OSUnMap數組中的值就是從0x00到0xFF的八位中,每一個值所對應的最低位的值。我們在調度的時候只需將OSRdyGrp的值代入OSUnMapTbl數組中,得到OSUnMapTbl[OSRdyGrp]的值就是哪個優先級最高的Group有Ready進程存在,再使用該Group對應OSRdyTbl[]數組中的值一樣帶入OSUnMapTbl中就可以得出哪個Task是優先級最高的。
於是我們提前來看看OS_Sched()中獲取最高優先級所使用的方法:
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
INT8U const OSMapTbl[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
INT8U const OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
OSMapTbl分別是一個INT8U的八個位,而OSUnMap數組中的值就是從0x00到0xFF的八位中,每一個值所對應的最低位的值。我們在調度的時候只需將OSRdyGrp的值代入OSUnMapTbl數組中,得到OSUnMapTbl[OSRdyGrp]的值就是哪個優先級最高的Group有Ready進程存在,再使用該Group對應OSRdyTbl[]數組中的值一樣帶入OSUnMapTbl中就可以得出哪個Task是優先級最高的。
於是我們提前來看看OS_Sched()中獲取最高優先級所使用的方法:
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
顯然,先得到的y就是存在最高優先級的Group,然後OSUnMapTbl[OSRdyTbl[y]]就是Group中的偏移,因此OSPrioHighRdy最高優先級就應該是Group<<3再加上這個偏移。
於是乎,我們就可以對上面那一小段很模糊的代碼做一下總結:
prio只有6位,高3位代表着某一個Group保存在OSTCBY中,OSTCBBitY表示該Group所對應的Bit,將OSRdyGrp的該位置1表示該Group中有進程是Ready的;低3位代表着該Group中的第幾個進程,保存在OSTCBX中,OSTCBBitX表示該進程在該Group中所對應的Bit,OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX就等於將該進程所對應的Bit置1了。
於是乎,我們就可以對上面那一小段很模糊的代碼做一下總結:
prio只有6位,高3位代表着某一個Group保存在OSTCBY中,OSTCBBitY表示該Group所對應的Bit,將OSRdyGrp的該位置1表示該Group中有進程是Ready的;低3位代表着該Group中的第幾個進程,保存在OSTCBX中,OSTCBBitX表示該進程在該Group中所對應的Bit,OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX就等於將該進程所對應的Bit置1了。
OSStart
OK,接下來我們來看這個開始函數了。OSStart其實很短,只有匆匆幾句代碼:
void OSStart (void)
{
INT8U y;
INT8U x;
void OSStart (void)
{
INT8U y;
INT8U x;
if (OSRunning == FALSE) {
y = OSUnMapTbl[OSRdyGrp]; /* Find highest priority's task priority number */
x = OSUnMapTbl[OSRdyTbl[y]];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy(); /* Execute target specific code to start task */
}
}
如果OSRunning爲TRUE,表示OS已經在運行了,則OSStart不做任何事。
OSRunning爲FALSE,則找出最高優先級的Ready的Task,並將該指針賦給OSTCBHighRdy和OSTCBCur。然後調用OSStartHighRdy()開始運行該進程。
OSStartHighRdy()爲用戶自定義函數,在這個函數中,主要功能就是進行堆棧切換並將OSRunning設置爲TRUE表示OS已經開始運行,然後將保存的寄存器彈出,最後執行中斷返回指令IRET就跳到OSTCBHighRdy的最開始處運行了。
OSTimeDly
在Task中,一般執行一段時間之後調用OSTimeDly推遲一段時間再繼續運行,OSTimeDly將本進程從Ready TCBList中刪除,然後將Delay的時間設置給OSTCBDly,最後調用OS_Sched進行進程調度。
void OSTimeDly (INT16U ticks)
{
INT8U y;
if (ticks > 0) { /* 0 means no delay! */
OS_ENTER_CRITICAL();
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next task to run! */
}
}
{
INT8U y;
if (ticks > 0) { /* 0 means no delay! */
OS_ENTER_CRITICAL();
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next task to run! */
}
}
如果ticks爲零,說明不需延遲,則什麼事情都不做。否則,調用OS_ENTER_CRITICAL進入臨界段,將本進程從Ready TCBList中刪除的代碼如下:
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
y爲當前進程所在Group,OSRdyTbl[y]爲該Group所在字節,&=~則將該字節中本進程所佔用的Bit清零。如果OSRdyTbl[y]爲0,則說明這個Group中沒有進程處於Ready狀態,則將OSRdyGrp中該Group所佔用的Bit清零。
y = OSTCBCur->OSTCBY; /* Delay current task */
OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0) {
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
y爲當前進程所在Group,OSRdyTbl[y]爲該Group所在字節,&=~則將該字節中本進程所佔用的Bit清零。如果OSRdyTbl[y]爲0,則說明這個Group中沒有進程處於Ready狀態,則將OSRdyGrp中該Group所佔用的Bit清零。
然後將ticks保存在OSTCBDly中,每次OSTimeTick運行時會將這個值減一直至爲零。
調用OS_EXIT_CRITICAL離開臨界段,緊接着調用OS_Sched進入調度例程。
調用OS_EXIT_CRITICAL離開臨界段,緊接着調用OS_Sched進入調度例程。
OS_Sched
OS_Sched是進程調度所使用的函數,在這裏面找到最高優先級的進程,然後切換到該進程運行。
void OS_Sched (void)
{
INT8U y;
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0) { /* ... scheduler is not locked */
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
void OS_Sched (void)
{
INT8U y;
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */
if (OSLockNesting == 0) { /* ... scheduler is not locked */
y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++; /* Increment context switch counter */
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
OS_TASK_SW(); /* Perform a context switch */
}
}
}
OS_EXIT_CRITICAL();
}
OS_Sched不允許在中斷嵌套中調用,因此先判斷是否是中斷嵌套,並且是否限制進程調度,這兩個條件都滿足之後,找到最高優先級的進程,如果這個進程不是當前進程,則將新的進程TCB指針保存到OSTCBHighRdy中,爲調度計數器OSCtxSwCtr加一,然後調用宏OS_TASK_SW()進行切換。
OS_TASK_SW()宏也是一個自定義的宏,uC/OS-II推薦使用軟中斷方式實現。
OSCtxSw是一箇中斷響應函數,一般我們在初始化時將這個軟終端和OSCtxSw掛接好。在OSCtxSw中所需要做的事情就是將當前寄存器的值保存到當前堆棧中,然後切換堆棧到新進程的堆棧,將寄存器的值出棧,然後調用中斷返回指令IRET就返回到新進程中斷前的地方繼續執行了。
OSCtxSw是一箇中斷響應函數,一般我們在初始化時將這個軟終端和OSCtxSw掛接好。在OSCtxSw中所需要做的事情就是將當前寄存器的值保存到當前堆棧中,然後切換堆棧到新進程的堆棧,將寄存器的值出棧,然後調用中斷返回指令IRET就返回到新進程中斷前的地方繼續執行了。
定時中斷
uC/OS-II的定時中斷必須在OSStart之後初始化,而不能在OSStart之前,因爲害怕第一個TimeTick發生時第一個進程還沒有開始運行,而這時uC/OS是處於不可預期狀態,會導致死機。
因此對於定時中斷,我一般是放在最高級進程的初始化中進行,然後將定時中斷和OSTickISR掛接。
OSTickISR也是一個用戶自定義函數,所要完成的功能一個是保存當前的寄存器到當前堆棧將OSIntNesting加一,然後調用uC/OS提供的OSTimeTick函數,然後調用OSIntExit()將OSIntNesting減一,最後將各寄存器值出棧,使用中斷返回指令IRET返回。
OSTickISR也是一個用戶自定義函數,所要完成的功能一個是保存當前的寄存器到當前堆棧將OSIntNesting加一,然後調用uC/OS提供的OSTimeTick函數,然後調用OSIntExit()將OSIntNesting減一,最後將各寄存器值出棧,使用中斷返回指令IRET返回。
OSTimeTick在每個時鐘中斷中被調用一次,在該函數中會更新各個進程TCB所對應的OSTCBDly,如果該OSTCBDly減爲0,則對應的TCB就被放入Ready TCBList中。
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* No, Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
/* Check for timeout */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */
ptcb->OSTCBPendTO = TRUE; /* Indicate PEND timeout */
} else {
ptcb->OSTCBPendTO = FALSE;
}
OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
OSTime++;
OS_EXIT_CRITICAL();
ptcb = OSTCBList; /* Point at first TCB in TCB list */
while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
OS_ENTER_CRITICAL();
if (ptcb->OSTCBDly != 0) { /* No, Delayed or waiting for event with TO */
if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
/* Check for timeout */
if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {
ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */
ptcb->OSTCBPendTO = TRUE; /* Indicate PEND timeout */
} else {
ptcb->OSTCBPendTO = FALSE;
}
if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
}
ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
OS_EXIT_CRITICAL();
}
首先在臨界段將OSTime加一,然後遍歷整個非Free的TCBList,如果OSTCBDly不爲0,則,將OSTCBDly減一,如果這時OSTCBDly爲0,而且TCB對應的進程需要等待任何信號量或Event等,則說明超時時間到了,將當前TCB的State中OS_STAT_PEND_ANY位去掉,然後將OSTCBPendTo設置爲TRUE,表示這是PEND的超時,否則設置OSTCBPendTO爲FALSE。
如果OSTCBDly減爲零,且該進程沒有Suspend,則將該進程放入Ready TCBList中,使用方法同TaskCreate中的方法。
如果OSTCBDly減爲零,且該進程沒有Suspend,則將該進程放入Ready TCBList中,使用方法同TaskCreate中的方法。
然後我們來說說OSIntExit這個函數。該函數代碼如下:
void OSIntExit (void)
{
INT8U y;
if (OSRunning == TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
if (OSIntNesting == 0) { /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0) { /* ... and not locked. */
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
{
INT8U y;
if (OSRunning == TRUE) {
OS_ENTER_CRITICAL();
if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */
OSIntNesting--;
}
if (OSIntNesting == 0) { /* Reschedule only if all ISRs complete ... */
if (OSLockNesting == 0) { /* ... and not locked. */
y = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
OSCtxSwCtr++; /* Keep track of the number of ctx switches */
OSIntCtxSw(); /* Perform interrupt level ctx switch */
}
}
}
OS_EXIT_CRITICAL();
}
}
}
}
}
OS_EXIT_CRITICAL();
}
}
首先判斷OSRunning是否爲1,也就是OS是否在運行,當然沒有運行就什麼都不做。
然後將OSIntNesting減一,這個是需要在臨界段進行的。如果OSIntNesting減爲零,並且沒有限制進程切換,則找到當前最高優先級的進程(方法同OS_Sched()),然後調用OSIntCtxSw進行進程切換。
然後將OSIntNesting減一,這個是需要在臨界段進行的。如果OSIntNesting減爲零,並且沒有限制進程切換,則找到當前最高優先級的進程(方法同OS_Sched()),然後調用OSIntCtxSw進行進程切換。
OSIntCtxSw()是用戶自定義函數,該函數的主要功能與OSCtxSw類似,只是需要對當前的堆棧進行稍微的調整,將OSIntExit和OSIntCtxSw調用所需要的堆棧去掉,然後做的和OSCtxSw一樣。
在實際的Porting中發現要去掉OSIntExit和OSIntCtxSw調用所佔用的堆棧還是比較麻煩的,因此我就現在OSTickISR剛開始的時候保存好現場之後就將堆棧指針賦給當前進程TCB的OSStkPtr,這樣,在OSIntCtxSw中就不需要重新對當前堆棧的值進行保存,只需進行切換就可以了。
在實際的Porting中發現要去掉OSIntExit和OSIntCtxSw調用所佔用的堆棧還是比較麻煩的,因此我就現在OSTickISR剛開始的時候保存好現場之後就將堆棧指針賦給當前進程TCB的OSStkPtr,這樣,在OSIntCtxSw中就不需要重新對當前堆棧的值進行保存,只需進行切換就可以了。