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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

uCos的多任务实现

發布時間:2024/6/21 综合教程 32 生活家
生活随笔 收集整理的這篇文章主要介紹了 uCos的多任务实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

uCos的多任務實現

作為操作系統(OS),最基本的一項服務就是提供多線程,在實時操作系統uCos里,多線程被稱為多任務(Task)。多任務并不是CPU能真正同時運行多個程序,實際是靠CPU在多個任務之間轉換切換實現的,CPU輪番的服務于一系列的任務,這樣CPU在宏觀上好像在同時執行多個任務,實際在微觀上CPU絕對是“單任務”的。這里要注意區別多線程和多核,如果系統里是有多個CPU,則可以實現真正的多線程了。

按照上面的思路,多任務的實現,就是要實現CPU在不同的任務之間切換。按照uCos作者的話說:“就是不斷的保存,恢復CPU的那些寄存器”。

我們知道uCos的多任務(這里以兩個任務為例)的程序模型如下:

voidTask_A(void *p_arg)

{

while(1)

{

OSTimeDly(10); //任務里必須有類似的主動釋放CPU的函數

......

}

}

voidTask_B(void *p_arg)

{

while(1)

{

OSTimeDly(10);

......

}

}

使用uCos,程序將在這兩個任務之間輪換,這兩個while(1)里的程序都可以得到執行。

我們知道,在裸機編程里,如果出現while(1)這樣的語句,那么程序將永遠在這個循環里執行(當然是要程序主動調用break除外),他是不會放棄CPU的,那為什么加了操作系統后,程序能在這兩個while(1)之間輪換?

操作系統都需要使用“時鐘節拍”技術來實現對任務的監控,并能主動調度和切換任務的執行。uCos也同樣是使用“時鐘節拍”來實現任務的監管的,以STM32單片機為例,一般用SysTick這個系統時鐘定時器來實現,比如我們設置這個定時器10ms的定時間隔,那么每隔10ms都會調用下面的中斷服務(所以移植的時候,需要實現這個函數):

void SysTickHandler(void)

{

OSIntEnter();

OSTimeTick();

OSIntExit();

}

假設現在程序在Task_A的while(1)里,然后SysTick中斷來了,SysTickHandler服務程序開始執行,先執行OSIntEnter():

voidOSIntEnter (void)

{

if (OSRunning == OS_TRUE) {

if (OSIntNesting < 255u) {

OSIntNesting++;

}

}

}

這個函數很簡單,他是uCos的內核函數,主要是給中斷嵌套計數器OSIntNesting加一,因為uCos內核需要實時的判斷程序的當前執行是不是在中斷里,要知道,大部分的處理器是可以中斷嵌套的,這里我們先不管判斷程序是不是在中斷里有什么用,后面馬上會說到。

然后開始執行OSTimeTick(),這個函數我們只分析關鍵代碼,也就是跟任務管理有關的代碼:

void OSTimeTick (void)

{

OS_TCB *ptcb;

#if OS_TICK_STEP_EN > 0

BOOLEAN step;

#endif

#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */

OS_CPU_SR cpu_sr = 0;

#endif

#if OS_TIME_TICK_HOOK_EN > 0

OSTimeTickHook(); /* Call user definable hook */

#endif

#if OS_TIME_GET_SET_EN > 0

OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */

OSTime++;

OS_EXIT_CRITICAL();

#endif

if (OSRunning == OS_TRUE) {

#if OS_TICK_STEP_EN > 0

switch (OSTickStepState) { /* Determine whether we need to process a tick */

case OS_TICK_STEP_DIS: /* Yes, stepping is disabled */

step = OS_TRUE;

break;

case OS_TICK_STEP_WAIT: /* No, waiting for uC/OS-View to set ... */

step = OS_FALSE; /* .. OSTickStepState to OS_TICK_STEP_ONCE */

break;

case OS_TICK_STEP_ONCE: /* Yes, process tick once and wait for next ... */

step = OS_TRUE; /* ... step command from uC/OS-View */

OSTickStepState = OS_TICK_STEP_WAIT;

break;

default: /* Invalid case, correct situation */

step = OS_TRUE;

OSTickStepState = OS_TICK_STEP_DIS;

break;

}

if (step == OS_FALSE) { /* Return if waiting for step command */

return;

}

#endif

ptcb = OSTCBList;/* Point at first TCB in TCB list */

while (ptcb->OSTCBPrio != OS_TASK_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 = OS_TRUE; /* Indicate PEND timeout */

} else {

ptcb->OSTCBPendTO = OS_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();

}

}

}

在這個函數里,出現了一個全局變量OSTCBList,這個變量是uCos內核里任務控制塊(TCB)鏈表的表頭指針,問題又來了,TCB是什么,TCB鏈表又是什么?

原來uCos的每個任務都需要有一個TCB來管理,這個TCB記錄了該任務的所有信息,同時uCos用鏈表來管理所有的這些TCB,TCB的結構如下:

typedef struct os_tcb {

OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */

#if OS_TASK_CREATE_EXT_EN > 0

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) */

#endif

struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */

struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */

#if OS_EVENT_EN

OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */

#endif

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

void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */

#endif

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

#if OS_TASK_DEL_EN > 0

OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */

#endif

OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */

#endif

INT16UOSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */

INT8U OSTCBStat; /* Task status */

BOOLEAN OSTCBPendTO; /* Flag indicating PEND timed out (OS_TRUE == timed out) */

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 */

#if OS_LOWEST_PRIO <= 63

INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */

INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */

#else

INT16U OSTCBBitX; /* Bit mask to access bit position in ready table */

INT16U OSTCBBitY; /* Bit mask to access bit position in ready group */

#endif

#if OS_TASK_DEL_EN > 0

INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */

#endif

#if OS_TASK_PROFILE_EN > 0

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 */

#endif

#if OS_TASK_NAME_SIZE > 1

INT8U OSTCBTaskName[OS_TASK_NAME_SIZE];

#endif

} OS_TCB;

這結構體稍微有點大,這里我們先知需要關心OSTCBDly這個成員,這個成員記錄該任務的延時值,當我們調用uCos的系統函數OSTimeDly(),OSQPend()等這些阻塞類型的函數時,該任務的TCB成員OSTCBDly就會被賦予相應值。

我們繼續分析OSTimeTick(),這個函數,在這個函數里,主要是給每個TCB的OSTCBDly的計數值減一,如果OSTCBDly為零了,則在任務就緒表里標記該任務,這里又引出了一問題,什么是就緒表,uCos的就緒表的實現用了一個比較巧妙的算法,這里先不仔細去分析,只有知道就緒表里記錄了當前系統中,哪些任務是處于就緒態,也就是可被CPU執行的狀態。

好,到這里OSTimeTick()分析完了,他完成了對每個任務的延時值減一操作,并更新了就緒表。下面開始執行OSIntExit(),這個函數是個很關鍵的函數,她將實現任務的上下文切換。

OSIntExit函數的源碼如下:

voidOSIntExit (void)

{

#if OS_CRITICAL_METHOD == 3

OS_CPU_SR cpu_sr = 0;

#endif

if (OSRunning == OS_TRUE) {/*如果uCos還沒有啟動運行,則不進行任務調度,因為這時候uCos的初始化還沒完成*/

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) { /*將中斷嵌套計數器減一*/

OSIntNesting--;

}

if (OSIntNesting == 0) {/*只有在中斷嵌套為0,也就是即將退出中斷時才進行任務調度*/

if (OSLockNesting == 0) {

OS_SchedNew();/*找出當前就緒表中的最高優先級任務,并更新全局變量OSPrioHighRdy */

if (OSPrioHighRdy != OSPrioCur) {/*如果發現當前最高優先級任務不是正在運行的任務,就要進行任務切換*/

OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];

#if OS_TASK_PROFILE_EN > 0

OSTCBHighRdy->OSTCBCtxSwCtr++;

#endif

OSCtxSwCtr++;

OSIntCtxSw(); /*調用專門用于中斷服務里的上下文切換函數,進行山下文的切換*/

}

}

}

OS_EXIT_CRITICAL();

}

}

從上面的源碼和代碼注釋可以總結出該函數完成的功能為:找出就緒表里的最高優先級任務,并執行OSIntCtxSw函數來進行任務切換。這里要注意,雖然執行了任務切換,但不會立刻進行上下文的切換,這個實現過程跟CPU的硬件有關,在STM32中,上下文的切換是用的“懸起中斷”,該中斷只有當CPU的所有中斷完成了,也就是退出了所有的中斷嵌套后,才會執行。OSIntCtxSw函數是移植uCos的一個非常關鍵的函數,他負責恢復運行任務的上次中斷現場,這個函數跟CPU體系有緊密的聯系,這里先不仔細分析。

到這里我們基本可以看到uCos利用系統時鐘滴答實現多任務的大概流程如下:

uCos的任務切換過程就分析跟蹤完了,這里要注意幾點,任務的切換并不只會發生在系統時鐘滴答的中斷服務里,調用發送信號量,發送消息等這些系統函數時也會引起任務切換,但大致的原理都差不多,這里我們只對程序的原理和框架做了說明,實際還是有些細節需要處理的。

總結

以上是生活随笔為你收集整理的uCos的多任务实现的全部內容,希望文章能夠幫你解決所遇到的問題。

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