UCOS源碼詳解


uC/OS-II源碼分析(總體思路 一)

首先從main函數開始,下面是uC/OS-II main函數的大致流程:

main()      {

OSInit();

TaskCreate(...);

OSStart();   }

首先是調用OSInit進行初始化,然后使用TaskCreate創建幾個進程/Task,最后調用OSStart,操作系統就開始運行了。

OSInit

最先看看OSInit完成哪些初始化:

void OSInit (void)

{

#if OS_VERSION >= 204

OSInitHookBegin();

#endif

OS_InitMisc();

OS_InitRdyList();

OS_InitTCBList();

OS_InitEventList();

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)

OS_FlagInit();

#endif

#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)

OS_MemInit();

#endif

#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)

OS_QInit();

#endif

OS_InitTaskIdle();

#if OS_TASK_STAT_EN > 0

OS_InitTaskStat();

#endif

#if OS_VERSION >= 204

OSInitHookEnd();

#endif

#if OS_VERSION >= 270 && OS_DEBUG_EN > 0

OSDebugInit();

#endif

}

OS_InitMisc()完成的是一些其其他他的變量的初始化:

OSIntNesting = 0;

OSLockNesting = 0;

OSTaskCtr = 0;

OSRunning = FALSE;

OSCtxSwCtr = 0;

OSIdleCtr =0L;

其中包括:中斷嵌套標志OSIntNesting,調度鎖定標志OSLockNesting,OS標志OSRunning等。OSRunning在這里設置為FALSE,在后面OSStartHighRdy中會被設置為TRUE表示OS開始工作。

OS_InitRdyList()初始化就緒Task列表:

static void OS_InitRdyList (void)

{

INT8U i;

INT8U *prdytbl;

OSRdyGrp = 0x00;

prdytbl = &OSRdyTbl[0];

for (i = 0; i < OS_RDY_TBL_SIZE; i++) {

*prdytbl++ = 0x00;

}

OSPrioCur = 0;

OSPrioHighRdy = 0;

OSTCBHighRdy = (OS_TCB *)0;

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;

OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl));

OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl));

ptcb1 = &OSTCBTbl[0];

ptcb2 = &OSTCBTbl[1];

for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) {

ptcb1->OSTCBNext = ptcb2;

#if OS_TASK_NAME_SIZE > 1

ptcb1->OSTCBTaskName[0] = '?';

ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;

#endif

ptcb1++;

ptcb2++;

}

ptcb1->OSTCBNext = (OS_TCB *)0;

#if OS_TASK_NAME_SIZE > 1

ptcb1->OSTCBTaskName[0] = '?';

ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL;

#endif

OSTCBList = (OS_TCB *)0;

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;

OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl));

pevent1 = &OSEventTbl[0];

pevent2 = &OSEventTbl[1];

for (i = 0; i < (OS_MAX_EVENTS - 1); i++) {

pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;

pevent1->OSEventPtr = pevent2;

#if OS_EVENT_NAME_SIZE > 1

pevent1->OSEventName[0] = '?';

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];

OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;

OSEventFreeList->OSEventPtr = (OS_EVENT *)0;

#if OS_EVENT_NAME_SIZE > 1

OSEventFreeList->OSEventName[0] = '?';

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,

&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],

OS_IDLE_PRIO,

OS_TASK_IDLE_ID,

&OSTaskIdleStk[0],

OS_TASK_IDLE_STK_SIZE,

(void *)0,

OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

其實Idle Task的初始化很簡單就是調用OSTaskCrete系列的函數創建一個Task, OSTaskCreate我們后面再做進一步分析。

初始化State Task也是類似調用OSTaskCreate系列函數創建Stat Task。這里只是創建了該Task的各個結構還沒有真正運行該Task,直到OSStart中才依據優先級調度運行。

OK,到這里OSInit算高一個段落了,我們接着回到main往下看

 

 

 

uC/OS-II源碼分析(總體思路 二)

 OSTaskCreate負責創建Task所需的數據結構,該函數原形如下所示:

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的優先級必須不同。

接下來,我們看看這個函數的執行流程:

#if OS_ARG_CHK_EN > 0

if (prio > OS_LOWEST_PRIO) {

return (OS_PRIO_INVALID);

}

#endif

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) {

OS_EXIT_CRITICAL();

return (OS_ERR_TASK_CREATE_ISR);

}

if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {

OSTCBPrioTbl[prio] = (OS_TCB *)1;

OS_EXIT_CRITICAL();

psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0);

err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);

if (err == OS_NO_ERR) {

if (OSRunning == TRUE) {

OS_Sched();

}

} else {

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio] = (OS_TCB *)0;

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()相對應,這個宏表示離開臨界段。

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進行進程調度;否則返回。

下面來看看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)

和OSTaskCreate類似,task是進程入口地址,pdata是參數地址,ptos是堆棧指針,而opt只是作為一個預留的參數Option而保留。返回的是調整以后的堆棧指針。

在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;

if (ptcb != (OS_TCB *)0) {

OSTCBFreeList = ptcb->OSTCBNext;

OS_EXIT_CRITICAL();

ptcb->OSTCBStkPtr = ptos;

ptcb->OSTCBPrio = prio;

ptcb->OSTCBStat = OS_STAT_RDY;

ptcb->OSTCBPendTO = FALSE;

ptcb->OSTCBDly = 0;

#if OS_TASK_CREATE_EXT_EN > 0

ptcb->OSTCBExtPtr = pext;

ptcb->OSTCBStkSize = stk_size;

ptcb->OSTCBStkBottom = pbos;

ptcb->OSTCBOpt = opt;

ptcb->OSTCBId = id;

#else

pext = pext;

stk_size = stk_size;

pbos = pbos;

opt = opt;

id = id;

#endif

#if OS_TASK_DEL_EN > 0

ptcb->OSTCBDelReq = OS_NO_ERR;

#endif

ptcb->OSTCBY = (INT8U)(prio >> 3);

ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];

ptcb->OSTCBX = (INT8U)(prio & 0x07);

ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];

#if OS_EVENT_EN

ptcb->OSTCBEventPtr = (OS_EVENT *)0;

#endif

OSTaskCreateHook(ptcb);

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio] = ptcb;

ptcb->OSTCBNext = OSTCBList;

ptcb->OSTCBPrev = (OS_TCB *)0;

if (OSTCBList != (OS_TCB *)0) {

OSTCBList->OSTCBPrev = ptcb;

}

OSTCBList = ptcb;

OSRdyGrp |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

OSTaskCtr++;

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的作用我們在等會專門來討論。

緊接着就要將該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位。

這里就涉及到OSMapTbl數組和OSUnMapTbl數組的用法了。我們先看看OSMapTbl和OSUnMapTbl的定義:

INT8U const OSMapTbl[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

INT8U const OSUnMapTbl[256] = {

從略………………

};

OSMapTbl分別是一個INT8U的八個位,而OSUnMap數組中的值就是從0x00到0xFF的八位中,每一個值所對應的最低位的值。我們在調度的時候只需將OSRdyGrp的值代入OSUnMapTbl數組中,得到OSUnMapTbl[OSRdyGrp]的值就是哪個優先級最高的Group有Ready進程存在,再使用該Group對應OSRdyTbl[]數組中的值一樣帶入OSUnMapTbl中就可以得出哪個Task是優先級最高的。

於是我們提前來看看OS_Sched()中獲取最高優先級所使用的方法:

y = OSUnMapTbl[OSRdyGrp];

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了。

OSStart

OK,接下來我們來看這個開始函數了。OSStart其實很短,只有匆匆幾句代碼:

void OSStart (void)

{

INT8U y;

INT8U x;

if (OSRunning == FALSE) {

y = OSUnMapTbl[OSRdyGrp];

x = OSUnMapTbl[OSRdyTbl[y]];

OSPrioHighRdy = (INT8U)((y << 3) + x);

OSPrioCur = OSPrioHighRdy;

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

OSTCBCur = OSTCBHighRdy;

OSStartHighRdy();

}

}

如果OSRunning為TRUE,表示OS已經在運行了,則OSStart不做任何事。

OSRunning為FALSE,則找出最高優先級的Ready的Task,並將該指針賦給OSTCBHighRdy和OSTCBCur。然后調用OSStartHighRdy()開始運行該進程。

OSStartHighRdy()為用戶自定義函數,在這個函數中,主要功能就是進行堆棧切換並將OSRunning設置為TRUE表示OS已經開始運行,然后將保存的寄存器彈出,最后執行中斷返回指令IRET就跳到OSTCBHighRdy的最開始處運行了。

 

 

uC/OS-II源碼分析(總體思路 三)

 

OSTimeDly

在Task中,一般執行一段時間之后調用OSTimeDly推遲一段時間再繼續運行,OSTimeDly將本進程從Ready TCBList中刪除,然后將Delay的時間設置給OSTCBDly,最后調用OS_Sched進行進程調度。

void OSTimeDly (INT16U ticks)

{

INT8U y;

if (ticks > 0) {

OS_ENTER_CRITICAL();

y = OSTCBCur->OSTCBY;

OSRdyTbl[y] &= ~OSTCBCur->OSTCBBitX;

if (OSRdyTbl[y] == 0) {

OSRdyGrp &= ~OSTCBCur->OSTCBBitY;

}

OSTCBCur->OSTCBDly = ticks;

OS_EXIT_CRITICAL();

OS_Sched();

}

}

如果ticks為零,說明不需延遲,則什么事情都不做。否則,調用OS_ENTER_CRITICAL進入臨界段,將本進程從Ready TCBList中刪除的代碼如下:

y = OSTCBCur->OSTCBY;

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_Sched

OS_Sched是進程調度所使用的函數,在這里面找到最高優先級的進程,然后切換到該進程運行。

void OS_Sched (void)

{

INT8U y;

OS_ENTER_CRITICAL();

if (OSIntNesting == 0) {

if (OSLockNesting == 0) {

y = OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

if (OSPrioHighRdy != OSPrioCur) {

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;

OS_TASK_SW();

}

}

}

OS_EXIT_CRITICAL();

}

OS_Sched不允許在中斷嵌套中調用,因此先判斷是否是中斷嵌套,並且是否限制進程調度,這兩個條件都滿足之后,找到最高優先級的進程,如果這個進程不是當前進程,則將新的進程TCB指針保存到OSTCBHighRdy中,為調度計數器OSCtxSwCtr加一,然后調用宏OS_TASK_SW()進行切換。

OS_TASK_SW()宏也是一個自定義的宏,uC/OS-II推薦使用軟中斷方式實現。

OSCtxSw是一個中斷響應函數,一般我們在初始化時將這個軟終端和OSCtxSw掛接好。在OSCtxSw中所需要做的事情就是將當前寄存器的值保存到當前堆棧中,然后切換堆棧到新進程的堆棧,將寄存器的值出棧,然后調用中斷返回指令IRET就返回到新進程中斷前的地方繼續執行了。

定時中斷

uC/OS-II的定時中斷必須在OSStart之后初始化,而不能在OSStart之前,因為害怕第一個TimeTick發生時第一個進程還沒有開始運行,而這時uC/OS是處於不可預期狀態,會導致死機。

因此對於定時中斷,我一般是放在最高級進程的初始化中進行,然后將定時中斷和OSTickISR掛接。

OSTickISR也是一個用戶自定義函數,所要完成的功能一個是保存當前的寄存器到當前堆棧將OSIntNesting加一,然后調用uC/OS提供的OSTimeTick函數,然后調用OSIntExit()將OSIntNesting減一,最后將各寄存器值出棧,使用中斷返回指令IRET返回。

OSTimeTick在每個時鍾中斷中被調用一次,在該函數中會更新各個進程TCB所對應的OSTCBDly,如果該OSTCBDly減為0,則對應的TCB就被放入Ready TCBList中。

OS_ENTER_CRITICAL();

OSTime++;

OS_EXIT_CRITICAL();

ptcb = OSTCBList;

while (ptcb->OSTCBPrio != OS_IDLE_PRIO) {

OS_ENTER_CRITICAL();

if (ptcb->OSTCBDly != 0) {

if (--ptcb->OSTCBDly == 0) {

if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {

ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY;

ptcb->OSTCBPendTO = TRUE;

} else {

ptcb->OSTCBPendTO = FALSE;

}

if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {

OSRdyGrp |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

}

}

}

ptcb = ptcb->OSTCBNext;

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中的方法。

然后我們來說說OSIntExit這個函數。該函數代碼如下:

void OSIntExit (void)

{

INT8U y;

if (OSRunning == TRUE) {

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) {

OSIntNesting--;

}

if (OSIntNesting == 0) {

if (OSLockNesting == 0) {

y = OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

if (OSPrioHighRdy != OSPrioCur) {

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;

OSIntCtxSw();

}

}

}

OS_EXIT_CRITICAL();

}

}

首先判斷OSRunning是否為1,也就是OS是否在運行,當然沒有運行就什么都不做。

然后將OSIntNesting減一,這個是需要在臨界段進行的。如果OSIntNesting減為零,並且沒有限制進程切換,則找到當前最高優先級的進程(方法同OS_Sched()),然后調用OSIntCtxSw進行進程切換。

OSIntCtxSw()是用戶自定義函數,該函數的主要功能與OSCtxSw類似,只是需要對當前的堆棧進行稍微的調整,將OSIntExit和OSIntCtxSw調用所需要的堆棧去掉,然后做的和OSCtxSw一樣。

在實際的Porting中發現要去掉OSIntExit和OSIntCtxSw調用所占用的堆棧還是比較麻煩的,因此我就現在OSTickISR剛開始的時候保存好現場之后就將堆棧指針賦給當前進程TCB的OSStkPtr,這樣,在OSIntCtxSw中就不需要重新對當前堆棧的值進行保存,只需進行切換就可以了

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

堆棧結構

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM