1. 消息郵箱Mbox
Mbox用於多任務間單一消息的傳遞,uC/OS-II使用ECB管理Mbox的基本信息,OSEventPtr指向創建Mbox時指定的內存空間。事件的創建由具體的事件管理程序實現。主要包含在C源文件OS_MBOX.C中。
OS_EVENT *OSMboxCreate(void *msg);
void *OSMboxPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
void *OSMboxAccept(OS_EVENT *pevent);
INT8U OSMboxPost(OS_EVENT *pevent, void *msg);
INT8U OSMboxPostOpt(OS_EVENT *pevent, void *msg, INT8U opt);
OS_EVENT *OSMboxDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
INT8U OSMboxQuery(OS_EVENT *pevent, OS_MBOX_DATA *);
2. 消息隊列msgQ
(1) msgQ基本內容
msgQ是uC/OS-II任務間通信的機制,可實現多條消息傳遞,即可以同時存儲多條消息。uC/OS-II使用循環隊列管理機制。主要包含在C源文件OS_Q.C中。
msgQ管理:使用指針數組存儲所有消息的位置;使用QCB標識指針數組中消息的基本信息;使用ECB管理整個msgQ。QCB在編譯時分配空間,即當前系統中可用的msgQ個數是預先設置的,系統運行時不能修改。
(2) msgQ全局變量
OS_EXT OS_Q *OSQTbl[OS_MAX_QS]; //QCB結構體數組
OS_EXT OS_Q *OSQFreeList; //空閒QCB頭指針
typedef struct os_q{ //消息隊列控制塊
struct os_q *OSQPtr; //用於構建空閒QCB鏈表
void **OSQStart; //指向msgQ指針數組的起始位置
void **OSQEnd; //指向msgQ指針數組的結束位置
void **OSQIn; //指向msgQ指針數組下一個可以插入消息的位置
void **OSQOut; //指向msgQ指針數組下一個可以讀出消息的位置
INT16U OSQSize; //msgQ指針數組的大小
INT16U OSQEntries; //msgQ指針數組當前可以讀取的消息個數
}OS_Q;
(3) msgQ管理函數
OS_EVENT *OSQCreate(void **start, INT16U size);
INT8U OSQPost(OS_EVENT *pevent, void *msg); //發送消息到隊尾
INT8U OSQPostFront(OS_EVENT *pevent, void *msg); //msg至隊首
INT8U OSQPostOpt(OS_EVENT *pevent, void *msg, INT8U opt);
void *OSQPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
void *OSQAccept(OS_EVENT *pevent, INT8U *err);
OS_EVENT *OSQDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
INT8U OSQQuery(OS_EVENT *pevent, OS_Q_DATA*);
INT8U OSQFlush(OS_EVENT *pevent);
(4) msgQ幾個問題
uC/OS-II中,什麼是事件?事件是uC/OS-II管理任務間同步與通信的機制。
事件是處理事件的對象感興趣的,能夠感知或捕獲到一種事件狀態的改變。
3. 信號量Sem
Sem主要用來實現任務間同步及標識某類資源的可用個數,即某個特定資源可供多少任務同時使用。主要包含在C源文件OS_SEM.C中。
OS_EVENT *OSSemCreate(INT16U cnt);
void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
INT16U OSSemAccept(OS_EVENT *pevent);
INT8U OSSemPost(OS_EVENT *pevent);
OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA*);
void OSSemSet(OS_EVENT *pevent, INT16U cnt, INT8U *err);
4. 互斥鎖Mutex
(1) Mutex基本原理
Mutex用來實現對資源的排他性訪問,可能引起優先級反轉。任何任務在佔有某個互斥鎖事件時,都不能阻塞等待其它任何事件,否則會造成死鎖。主要包含在C源文件OS_MUTEX.C中。
優先級反轉是指,低優先級任務佔有高優先級任務運行所需的資源,而使高優先級不得不等低優先級任務把資源釋放才能執行。
uC/OS-II使用ECB管理Mutex,其成員變量OSEventCnt:高8位存儲Mutex被使用時提供給任務的prio;低8位在沒有任務佔有Mutex時爲0xFF,否則爲佔有任務的prio。
優先級反轉及優先級反轉避免分別如下圖所示:
(2) 提升/恢復優先級
a) 提升Mutex擁有者任務的優先級的相關操作:
如果該任務原來處於就緒狀態,則從就緒表中將其刪除;如果該任務正在等待某個事件,則從該事件的任務等待表中將其刪除;
修改擁有Mutex的TCB,將其OSTCBPrio修改爲欲提升的優先級;
如果該任務處於就緒狀態,則將提升的優先級加載到任務就緒表中;如果該任務未就緒且正在等待某個事件,則將提升的優先級添加到該事件的任務等待表中,並修改TCB中OSTCBEventPtr;
修改TCB中與優先級相關的成員變量。
b) 恢復Mutex擁有任務的優先級的相關操作:
從任務就緒表中刪除提升過的優先級值,修改當前TCB中與優先級相關的所有成員變量;
再次保留提升的優先級值控制塊入口,避免將其分配給其它任務。
(3)Mutex管理函數
#define OS_MUTEX_KEEP_LOWER_8 0x00FF
#define OS_MUTEX_KEEP_UPPER_8 0xFF00
#define OS_MUTEX_AVAILABLE 0x00FF
OS_EVENT *OSMutexCreate(INT8U prio, INT8U *err);
void OSMutexPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
INT8U OSMutexAccept(OS_EVENT *pevent, INT8U *err);
INT8U OSMutexPost(OS_EVENT *pevent);
OS_EVENT *OSMutexDel(OS_EVENT*, INT8U opt, INT8U *err);
INT8U OSMutexQuery(OS_EVENT*, OS_MUTEX_DATA*);
5. 事件組標誌Flag
(1) Flag基本原理
uC/OS-II提供事件組標誌實現多事件管理。Flag只是使用0/1來表示某個事件是否發生過,而不能直接被用來傳遞數據和消息。可以選擇性地設置一個Flag最多可以管理的任務同步狀態。主要包含在C源文件OS_FLAG.C中。
(2) Flag數據結構
#define OS_FLAGS_NBITS 8/16/32 //定義OS_FLAGS的位數
FCB結構體:
typedef struct os_flag_grp{
INT8U OSFlagType; //事件類型
void *OSFlagWaitList; //指向等待的任務鏈表
OS_FLAGS OSFlagFlags; //信號列表
INT8U OSFlagName[OS_FLAG_NAME_SIZE];
}OS_FLAG_GRP;
事件標誌等待鏈表結點
typedef struct os_flag_node{
void *OSFlagNodeNext;
void *OSFlagNodePrev;
void *OSFlagNodeTCB;
void *OSFlagNodeFlagGrp; //指向此任務所等待的事件組標誌
OS_FLAGS OSFlagNodeFlags; //等待的事件
INT8U OSFlagNodeWaitType; //等待方式
}OS_FLAG_NODE;
OS_EXT OS_FLAG_GRP OSFlagTbl[OS_MAX_FLAGS];
OS_EXT OS_FLAG_GRP *OSFlagFreeList;
OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *err);
OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags,
(3) Flag管理函數
INT8U wait_type, INT16U timeout, INT8U *err);
static void OS_FlagBlock(OS_FLAG_GRP *pgrp,
OS_FLAG_NODE *pnode,
OS_FLAGS flags,
INT8U wait_type, //掛起任務,
INT16U timeout); //直到等待的事件或超時
類似於:OS_EventTaskWait();
void OS_FlagUnlink(OS_FLAG_NODE *pnode); //等待超時刪除結點
類似於:OS_EventTO();
OS_FLAGS OSFlagAccept(OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U wait_type,
INT8U *err);
OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp,
OS_FLAGS flags,
INT8U opt,
INT8U *err);
static BOOLEAN OS_FLAGTaskRdy(OS_FLAG_NODE *pnode,
OS_FLAGS flags_rdy);
OS_FLAG_GRP *OSFlagDel(OS_FLAG_GRP*, INT8U opt, INT8U *err);
OS_FLAGS OSFlagPendGetFlagsRdy(void); //獲取任務就緒標誌
OS_FLAGS OSFlagQuery(OS_FLAG_GRP*, INT8U *err);
INT8U OSFlagNameGet(OS_FLAG_GRP*, INT8U *pname, INT8U *err);
void OSFlagNameSet(OS_FLAG_GRP*, INT8U *pname, INT8U *err);
6. Task就緒狀態判斷???
a) OSRdyTbl[ptcb->OSTCBY] & ptcb->OSTCBBitX != 0
如:函數OSMutexPend()
b) (ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY
如:函數OSTimeTick()
c) ptcb->OSTCBStat == OS_STAT_RDY
如:函數OS_EventTaskRdy()