日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

uC/OS-II源码分析(总体思路 二)

發布時間:2023/12/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uC/OS-II源码分析(总体思路 二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OSTaskCreate

?

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] = {

???? 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,??????

???? 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0???????

?};

?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源码分析(总体思路 二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。