百度網(wǎng)站做防水補(bǔ)漏seo01
寫在前面:
由于時(shí)間的不足與學(xué)習(xí)的碎片化,寫博客變得有些奢侈。
但是對于記錄學(xué)習(xí)(忘了以后能快速復(fù)習(xí))的渴望一天天變得強(qiáng)烈。
既然如此
不如以天為單位,以時(shí)間為順序,僅僅將博客當(dāng)做一個(gè)知識學(xué)習(xí)的目錄,記錄筆者認(rèn)為最通俗、最有幫助的資料,并盡量總結(jié)幾句話指明本質(zhì),以便于日后搜索起來更加容易。
標(biāo)題的結(jié)構(gòu)如下:“類型”:“知識點(diǎn)”——“簡短的解釋”
部分內(nèi)容由于保密協(xié)議無法上傳。
點(diǎn)擊此處進(jìn)入學(xué)習(xí)日記的總目錄
2024.03.31:UCOSIII第二十八節(jié):消息隊(duì)列常用函數(shù)
- 四十二、UCOSIII:消息隊(duì)列常用函數(shù)
- 1、創(chuàng)建消息隊(duì)列函數(shù)OSQCreate()
- 2、消息隊(duì)列刪除函數(shù)OSQDel()
- 3、消息隊(duì)列發(fā)送函數(shù)OSQPost()
- 1. OSQPost()函數(shù)
- 2. OS_QPost()函數(shù)
- 3. OS_MsgQPut()函數(shù)
- 4. OS_Post()函數(shù)
- 4、消息隊(duì)列獲取函數(shù)OSQPend()
- 1. OSQPend()函數(shù)
- 2. OS_MsgQGet()函數(shù)
- 3. OS_Pend()函數(shù)
四十二、UCOSIII:消息隊(duì)列常用函數(shù)
1、創(chuàng)建消息隊(duì)列函數(shù)OSQCreate()
要使用 μC/OS的消息隊(duì)列必須先聲明和創(chuàng)建消息隊(duì)列,OSQCreate()用于創(chuàng)建一個(gè)新的隊(duì)列。
隊(duì)列就是一個(gè)數(shù)據(jù)結(jié)構(gòu), 用于任務(wù)間的數(shù)據(jù)的傳遞。每創(chuàng)建一個(gè)新的隊(duì)列都需要為其分配RAM,在創(chuàng)建的時(shí)候我們需要自己定義一個(gè)消息隊(duì)列結(jié)構(gòu)體, 其內(nèi)存是由編譯器自動分配的。
OSQCreate的源碼具體如下
void OSQCreate (OS_Q *p_q, //(1) //消息隊(duì)列指針CPU_CHAR *p_name, //(2) //消息隊(duì)列名稱OS_MSG_QTY max_qty, //(3) //消息隊(duì)列大小(不能為0)OS_ERR *p_err) //(4) //返回錯(cuò)誤類型{CPU_SR_ALLOC();//(5)//使用到臨界段(在關(guān)/開中斷時(shí))時(shí)必須用到該宏,該宏聲明和//定義一個(gè)局部變量,用于保存關(guān)中斷前的 CPU 狀態(tài)寄存器// SR(臨界段關(guān)中斷只需保存SR),開中斷時(shí)將該值還原。#ifdef OS_SAFETY_CRITICAL//(6)//如果啟用了安全檢測if (p_err == (OS_ERR *)0) { //如果錯(cuò)誤類型實(shí)參為空OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return; //返回,停止執(zhí)行}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508 //如果啟用了安全關(guān)鍵
//如果在調(diào)用OSSafetyCriticalStart()后創(chuàng)建if (OSSafetyCriticalStartFlag == DEF_TRUE) {*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME; //錯(cuò)誤類型為“非法創(chuàng)建內(nèi)核對象”return; //返回,停止執(zhí)行}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u//(7)//如果啟用了中斷中非法調(diào)用檢測if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果該函數(shù)是在中斷中被調(diào)用*p_err = OS_ERR_CREATE_ISR; //錯(cuò)誤類型為“在中斷中創(chuàng)建對象”return; //返回,停止執(zhí)行}
#endif#if OS_CFG_ARG_CHK_EN > 0u//(8) //如果啟用了參數(shù)檢測if (p_q == (OS_Q *)0) { //如果 p_q 為空*p_err = OS_ERR_OBJ_PTR_NULL; //錯(cuò)誤類型為“創(chuàng)建對象為空”return; //返回,停止執(zhí)行}if (max_qty == (OS_MSG_QTY)0) { //(9)//如果 max_qty = 0*p_err = OS_ERR_Q_SIZE; //錯(cuò)誤類型為“隊(duì)列空間為0”return; //返回,停止執(zhí)行}
#endifOS_CRITICAL_ENTER(); //進(jìn)入臨界段p_q->Type = OS_OBJ_TYPE_Q; //(10)//標(biāo)記創(chuàng)建對象數(shù)據(jù)結(jié)構(gòu)為消息隊(duì)列p_q->NamePtr = p_name; //(11)//標(biāo)記消息隊(duì)列的名稱OS_MsgQInit(&p_q->MsgQ, //初始化消息隊(duì)列max_qty); //(12)OS_PendListInit(&p_q->PendList); //(13) //初始化該消息隊(duì)列的等待列表#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_QDbgListAdd(p_q); //將該隊(duì)列添加到消息隊(duì)列雙向調(diào)試鏈表
#endifOSQQty++; //(14)//消息隊(duì)列個(gè)數(shù)加1OS_CRITICAL_EXIT_NO_SCHED(); //退出臨界段(無調(diào)度)*p_err = OS_ERR_NONE; //錯(cuò)誤類型為“無錯(cuò)誤”
}
- (1):消息隊(duì)列指針,在創(chuàng)建之前我們要定義一個(gè)隊(duì)列的數(shù)據(jù)結(jié)構(gòu),然后將消息隊(duì)列指針指向該隊(duì)列。
- (2):消息隊(duì)列的名稱,字符串形式,這個(gè)名稱一般與消息隊(duì)列名稱一致,為了方便調(diào)試。
- (3):消息隊(duì)列的大小,也就是消息隊(duì)列的可用消息個(gè)數(shù)最大為多少,一旦確定無法修改。
- (4):用于保存返回的錯(cuò)誤類型。
- (5):使用到臨界段(在關(guān)/開中斷時(shí))時(shí)必須用到該宏,該宏聲明和定義一個(gè)局部變量, 用于保存關(guān)中斷前的 CPU 狀態(tài)寄存器SR(臨界段關(guān)中斷只需保存SR),開中斷時(shí)將該值還原。
- (6):如果啟用了安全檢測,在編譯時(shí)則會包含安全檢測相關(guān)的代碼,如果錯(cuò)誤類型實(shí)參為空, 系統(tǒng)會執(zhí)行安全檢測異常函數(shù),然后返回,停止執(zhí)行。
- (7):如果啟用了中斷中非法調(diào)用檢測,在編譯時(shí)則會包含中斷非法調(diào)用檢測相關(guān)的代碼, 如果該函數(shù)是在中斷中被調(diào)用,則是非法的,返回錯(cuò)誤類型為“在中斷中創(chuàng)建對象”的錯(cuò)誤代碼,并且退出,不執(zhí)行創(chuàng)建隊(duì)列操作。
- (8):如果啟用了參數(shù)檢測,在編譯時(shí)則會包含參數(shù)檢測相關(guān)的代碼, 如果 p_q 參數(shù)為空,返回錯(cuò)誤類型為“創(chuàng)建對象為空”的錯(cuò)誤代碼,并且退出,不執(zhí)行創(chuàng)建隊(duì)列操作。
- (9):如果 max_qty參數(shù)為 0,表示不存在消息空間,這也是錯(cuò)誤的, 返回錯(cuò)誤類型為“隊(duì)列空間為0”的錯(cuò)誤代碼,并且退出,不執(zhí)行創(chuàng)建隊(duì)列操作。
- (10):標(biāo)記創(chuàng)建對象數(shù)據(jù)結(jié)構(gòu)為消息隊(duì)列。
- (11):初始化消息隊(duì)列的名稱。
- (12):調(diào)用OS_MsgQInit()函數(shù)初始化消息隊(duì)列,其實(shí)就是初始化消息隊(duì)列結(jié)構(gòu)的相關(guān)信息
- (13):初始化消息隊(duì)列的阻塞列表,消息隊(duì)列的阻塞列表是用于記錄阻塞在此消息隊(duì)列上的任務(wù)。
- (14):OSQQty是系統(tǒng)中的一個(gè)全局變量, 用于記錄已經(jīng)創(chuàng)建的消息隊(duì)列個(gè)數(shù),現(xiàn)在創(chuàng)建隊(duì)列完畢,所以該變量要加一。
其中,OS_MsgQInit()源碼如下
void OS_MsgQInit (OS_MSG_Q *p_msg_q, //消息隊(duì)列指針OS_MSG_QTY size) //消息隊(duì)列空間
{p_msg_q->NbrEntriesSize = (OS_MSG_QTY)size; //消息隊(duì)列可存放消息數(shù)目p_msg_q->NbrEntries = (OS_MSG_QTY)0; //消息隊(duì)列目前可用消息數(shù)p_msg_q->NbrEntriesMax = (OS_MSG_QTY)0; //可用消息數(shù)的最大歷史記錄p_msg_q->InPtr = (OS_MSG *)0; //隊(duì)列的入隊(duì)指針p_msg_q->OutPtr = (OS_MSG *)0; //隊(duì)列的出隊(duì)指針
}
消息隊(duì)列創(chuàng)建完成的示意圖具體見圖
在創(chuàng)建消息隊(duì)列的時(shí)候,是需要用戶自己定義消息隊(duì)列的句柄的。
但是需要注意的是,定義了隊(duì)列的句柄并不等于創(chuàng)建了隊(duì)列。
創(chuàng)建隊(duì)列必須是調(diào)用消息隊(duì)列創(chuàng)建函數(shù)進(jìn)行創(chuàng)建,否則,以后根據(jù)隊(duì)列句柄使用消息隊(duì)列的其他函數(shù)的時(shí)候會發(fā)生錯(cuò)誤。
用戶通過消息隊(duì)列句柄就可使用消息隊(duì)列進(jìn)行發(fā)送與獲取消息的操作,用戶可以根據(jù)返回的錯(cuò)誤代碼進(jìn)行判斷消息隊(duì)列是否創(chuàng)建成功。
消息隊(duì)列創(chuàng)建函數(shù)OSQCreate()使用實(shí)例具體
OS_Q queue; //聲明消息隊(duì)列OS_ERR err;/* 創(chuàng)建消息隊(duì)列 queue */
OSQCreate ((OS_Q *)&queue, //指向消息隊(duì)列的指針(CPU_CHAR *)"Queue For Test", //隊(duì)列的名字(OS_MSG_QTY )20, //最多可存放消息的數(shù)目(OS_ERR *)&err); //返回錯(cuò)誤類型
2、消息隊(duì)列刪除函數(shù)OSQDel()
隊(duì)列刪除函數(shù)是根據(jù)隊(duì)列結(jié)構(gòu)(隊(duì)列句柄)直接刪除的,刪除之后這個(gè)消息隊(duì)列的所有信息都會被系統(tǒng)清空,而且不能再次使用這個(gè)消息隊(duì)列了。
需要注意的是,如果某個(gè)消息隊(duì)列沒有被定義,那也是無法被刪除的。
想要使用消息隊(duì)列刪除函數(shù)就必須將OS_CFG_Q_DEL_EN宏定義配置為1, 其函數(shù)源碼具體如下:
#if OS_CFG_Q_DEL_EN > 0u//如果啟用了 OSQDel() 函數(shù)
OS_OBJ_QTY OSQDel (OS_Q *p_q, //(1)//消息隊(duì)列指針OS_OPT opt, //(2)//選項(xiàng)OS_ERR *p_err) //(3)//返回錯(cuò)誤類型
{OS_OBJ_QTY cnt;OS_OBJ_QTY nbr_tasks;OS_PEND_DATA *p_pend_data;OS_PEND_LIST *p_pend_list;OS_TCB *p_tcb;CPU_TS ts;CPU_SR_ALLOC(); //使用到臨界段(在關(guān)/開中斷時(shí))時(shí)必須用到該宏,該宏聲明和//定義一個(gè)局部變量,用于保存關(guān)中斷前的 CPU 狀態(tài)寄存器// SR(臨界段關(guān)中斷只需保存SR),開中斷時(shí)將該值還原。#ifdef OS_SAFETY_CRITICAL //(4)//如果啟用(默認(rèn)禁用)了安全檢測if (p_err == (OS_ERR *)0) { //如果錯(cuò)誤類型實(shí)參為空OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return ((OS_OBJ_QTY)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //(5)//如果啟用了中斷中非法調(diào)用檢測if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果該函數(shù)在中斷中被調(diào)用*p_err = OS_ERR_DEL_ISR; //錯(cuò)誤類型為“在中斷中中止等待”return ((OS_OBJ_QTY)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_ARG_CHK_EN > 0u //(6)//如果啟用了參數(shù)檢測if (p_q == (OS_Q *)0) { //如果 p_q 為空*p_err = OS_ERR_OBJ_PTR_NULL; //錯(cuò)誤類型為“對象為空”return ((OS_OBJ_QTY)0u); //返回0(有錯(cuò)誤),停止執(zhí)行}switch (opt) { //(7)//根據(jù)選項(xiàng)分類處理case OS_OPT_DEL_NO_PEND: //如果選項(xiàng)在預(yù)期內(nèi)case OS_OPT_DEL_ALWAYS:break; //直接跳出default: //(8)*p_err = OS_ERR_OPT_INVALID; //如果選項(xiàng)超出預(yù)期return ((OS_OBJ_QTY)0u); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_OBJ_TYPE_CHK_EN > 0u //(9)//如果啟用了對象類型檢測if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息隊(duì)列類型*p_err = OS_ERR_OBJ_TYPE; //錯(cuò)誤類型為“對象類型有誤”return ((OS_OBJ_QTY)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endifCPU_CRITICAL_ENTER(); //關(guān)中斷p_pend_list = &p_q->PendList; //(10)//獲取消息隊(duì)列的等待列表cnt = p_pend_list->NbrEntries; //(11)//獲取等待該隊(duì)列的任務(wù)數(shù)nbr_tasks = cnt; //(12)//按照任務(wù)數(shù)目逐個(gè)處理switch (opt) { //(13)//根據(jù)選項(xiàng)分類處理case OS_OPT_DEL_NO_PEND: //(14)//如果只在沒有任務(wù)等待的情況下刪除隊(duì)列if (nbr_tasks == (OS_OBJ_QTY)0) {//(15)//如果沒有任務(wù)在等待該隊(duì)列
#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_QDbgListRemove(p_q); //將該隊(duì)列從消息隊(duì)列調(diào)試列表移除
#endifOSQQty--; //(16)//消息隊(duì)列數(shù)目減1OS_QClr(p_q); //(17)//清除該隊(duì)列的內(nèi)容CPU_CRITICAL_EXIT(); //開中斷*p_err = OS_ERR_NONE; //(18)//錯(cuò)誤類型為“無錯(cuò)誤”} else { //(19)//如果有任務(wù)在等待該隊(duì)列CPU_CRITICAL_EXIT(); //開中斷*p_err = OS_ERR_TASK_WAITING; //錯(cuò)誤類型為“有任務(wù)在等待該隊(duì)列”}
break;case OS_OPT_DEL_ALWAYS: //(20)//如果必須刪除信號量OS_CRITICAL_ENTER_CPU_EXIT(); //進(jìn)入臨界段,重開中斷ts = OS_TS_GET(); //獲取時(shí)間戳
while (cnt > 0u) { //(21)//逐個(gè)移除該隊(duì)列等待列表中的任務(wù)p_pend_data = p_pend_list->HeadPtr;p_tcb = p_pend_data->TCBPtr;OS_PendObjDel((OS_PEND_OBJ *)((void *)p_q),p_tcb,ts);cnt--; //(22)}
#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_QDbgListRemove(p_q); //將該隊(duì)列從消息隊(duì)列調(diào)試列表移除
#endifOSQQty--; //(23)//消息隊(duì)列數(shù)目減1OS_QClr(p_q); //(24)//清除消息隊(duì)列內(nèi)容OS_CRITICAL_EXIT_NO_SCHED(); //退出臨界段(無調(diào)度)OSSched(); //(25)//調(diào)度任務(wù)*p_err = OS_ERR_NONE; //(26)//錯(cuò)誤類型為“無錯(cuò)誤”break; //跳出default://(27)//如果選項(xiàng)超出預(yù)期CPU_CRITICAL_EXIT(); //開中斷*p_err = OS_ERR_OPT_INVALID; //錯(cuò)誤類型為“選項(xiàng)非法”break; //跳出}return (nbr_tasks); //返回刪除隊(duì)列前等待其的任務(wù)數(shù)
}
#endif
- (1):消息隊(duì)列指針,指向要刪除的消息隊(duì)列。
- (2):操作消息隊(duì)列的選項(xiàng),具體在后面講解。
- (3):用于保存返回的錯(cuò)誤類型。
- (4):如果啟用(默認(rèn)禁用)了安全檢測,在編譯時(shí)則會包含安全檢測相關(guān)的代碼,如果錯(cuò)誤類型實(shí)參為空, 系統(tǒng)會執(zhí)行安全檢測異常函數(shù),然后返回,停止執(zhí)行。
- (5):如果啟用了中斷中非法調(diào)用檢測,在編譯時(shí)則會包含中斷非法調(diào)用檢測相關(guān)的代碼, 如果該函數(shù)是在中斷中被調(diào)用,則是非法的,返回錯(cuò)誤類型為“在中斷中刪除”的錯(cuò)誤代碼,并且退出,不執(zhí)行刪除隊(duì)列操作。
- (6):如果啟用了參數(shù)檢測,在編譯時(shí)則會包含參數(shù)檢測相關(guān)的代碼,如果 p_q 參數(shù)為空, 返回錯(cuò)誤類型為“刪除對象為空”的錯(cuò)誤代碼,并且退出,不執(zhí)行刪除隊(duì)列操作。
- (7):根據(jù)選項(xiàng)分類處理,如果選項(xiàng)在預(yù)期內(nèi),直接跳出switch語句。
- (8):如果選項(xiàng)超出預(yù)期,就退出,不執(zhí)行刪除隊(duì)列操作。
- (9):如果啟用了對象類型檢測,在編譯時(shí)則會包含對象類型檢測相關(guān)代碼,如果 p_q 不是消息隊(duì)列類型, 那么返回錯(cuò)誤類型為“對象類型有誤”的錯(cuò)誤代碼,并且退出,不執(zhí)行刪除隊(duì)列操作。
- (10):程序能執(zhí)行到這里,說明傳入的參數(shù)都是正確的,此時(shí)可以執(zhí)行刪除隊(duì)列操作, 系統(tǒng)首先獲取消息隊(duì)列中的等待列表,通過p_pend_list變量進(jìn)行消息隊(duì)列等待列表的訪問。
- (11):獲取阻塞在該隊(duì)列上的任務(wù)個(gè)數(shù)。
- (12):按照任務(wù)數(shù)目逐個(gè)處理。
- (13):根據(jù)選項(xiàng)分類處理。
- (14):如果如果刪除選項(xiàng)是只在沒有任務(wù)等待的情況下刪除隊(duì)列,系統(tǒng)就會判斷有沒有任務(wù)阻塞在改隊(duì)列上。
- (15):如果沒有任務(wù)在等待該隊(duì)列,那就執(zhí)行刪除操作。
- (16):系統(tǒng)的消息隊(duì)列數(shù)目減1。
- (17):清除該隊(duì)列的內(nèi)容。
- (18):返回錯(cuò)誤類型為“無錯(cuò)誤”的錯(cuò)誤代碼。
- (19):而如果有任務(wù)在等待該隊(duì)列,那么就沒法進(jìn)行刪除操作,返回錯(cuò)誤類型為“有任務(wù)在等待該隊(duì)列”的錯(cuò)誤代碼。
- (20):如果刪除操作的選項(xiàng)是必須刪除消息隊(duì)列,無論是否有任務(wù)阻塞在該消息隊(duì)列上,系統(tǒng)都會進(jìn)行刪除操作。
- (21):根據(jù)消息隊(duì)列當(dāng)前等待的任務(wù)個(gè)數(shù),逐個(gè)移除該隊(duì)列等待列表中的任務(wù)。
- (22):調(diào)用OS_PendObjDel()函數(shù)將阻塞在內(nèi)核對象(如信號量)上的任務(wù)從阻塞態(tài)恢復(fù), 此時(shí)系統(tǒng)在刪除內(nèi)核對象, 刪除之后,這些等待事件的任務(wù)需要被恢復(fù)。每移除一個(gè),消息隊(duì)列的任務(wù)個(gè)數(shù)就減一, 當(dāng)沒有任務(wù)阻塞在該隊(duì)列上,就進(jìn)行刪除隊(duì)列操作。
- (23):系統(tǒng)的消息隊(duì)列數(shù)目減1。
- (24):清除消息隊(duì)列內(nèi)容。
- (25):發(fā)起一次調(diào)度任務(wù)。
- (26):返回錯(cuò)誤類型為“無錯(cuò)誤”的錯(cuò)誤代碼。
- (27):而如果選項(xiàng)超出預(yù)期,返回錯(cuò)誤類型為“選項(xiàng)非法”的錯(cuò)誤代碼,然后退出。
OS_PendObjDel()源碼如下
void OS_PendObjDel (OS_PEND_OBJ *p_obj, //(1) //被刪除對象的類型OS_TCB *p_tcb, //(2) //任務(wù)控制塊指針CPU_TS ts) //(3) //信號量被刪除時(shí)的時(shí)間戳
{
switch (p_tcb->TaskState) //(4)//根據(jù)任務(wù)狀態(tài)分類處理{case OS_TASK_STATE_RDY: //如果任務(wù)是就緒狀態(tài)case OS_TASK_STATE_DLY: //如果任務(wù)是延時(shí)狀態(tài)case OS_TASK_STATE_SUSPENDED: //如果任務(wù)是掛起狀態(tài)case OS_TASK_STATE_DLY_SUSPENDED: //如果任務(wù)是在延時(shí)中被掛起break; //(5)//這些情況均與等待無關(guān),直接跳出case OS_TASK_STATE_PEND: //如果任務(wù)是無期限等待狀態(tài)case OS_TASK_STATE_PEND_TIMEOUT: //如果任務(wù)是有期限等待狀態(tài)if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI)//如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列{OS_PendObjDel1(p_obj, //強(qiáng)制解除任務(wù)對某一對象的等待p_tcb,ts); //(6)}
#if (OS_MSG_EN > 0u) //(7)//如果啟用了任務(wù)隊(duì)列或消息隊(duì)列p_tcb->MsgPtr = (void *)0; //清除(復(fù)位)任務(wù)的消息域p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endifp_tcb->TS = ts; //(8)//保存等待被中止時(shí)的時(shí)間戳到任務(wù)控制塊OS_PendListRemove(p_tcb); //(9)//將任務(wù)從所有等待列表中移除OS_TaskRdy(p_tcb); //(10)//讓任務(wù)進(jìn)準(zhǔn)備運(yùn)行p_tcb->TaskState = OS_TASK_STATE_RDY; //(11)//修改任務(wù)狀態(tài)為就緒狀態(tài)p_tcb->PendStatus = OS_STATUS_PEND_DEL; //(12)//標(biāo)記任務(wù)的等待對象被刪除p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //(13)//標(biāo)記任務(wù)目前沒有等待任何對象break; //跳出case OS_TASK_STATE_PEND_SUSPENDED: //如果任務(wù)在無期限等待中被掛起case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED: //如果任務(wù)在有期限等待中被掛起if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI)//如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列{OS_PendObjDel1(p_obj, //強(qiáng)制解除任務(wù)對某一對象的等待p_tcb,ts); //(14)}
#if (OS_MSG_EN > 0u) //(15)//如果啟用了任務(wù)隊(duì)列或消息隊(duì)列p_tcb->MsgPtr = (void *)0; //(16)//清除(復(fù)位)任務(wù)的消息域p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endifp_tcb->TS = ts; //(17)//保存等待被中止時(shí)的時(shí)間戳到任務(wù)控制塊OS_TickListRemove(p_tcb); //(18)//讓任務(wù)脫離節(jié)拍列表OS_PendListRemove(p_tcb); //(19)//將任務(wù)從所有等待列表中移除p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; //(20)//修改任務(wù)狀態(tài)為掛起狀態(tài)p_tcb->PendStatus = OS_STATUS_PEND_DEL; //(21)//標(biāo)記任務(wù)的等待對象被刪除p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //標(biāo)記任務(wù)目前沒有等待任何對象break; //跳出default: //(22)//如果任務(wù)狀態(tài)超出預(yù)期break; //不需處理,直接跳出}
}
- (1):被刪除對象的類型(如消息隊(duì)列、信號量、互斥量、事件等)。
- (2):任務(wù)控制塊指針。
- (3):內(nèi)核對象被刪除時(shí)的時(shí)間戳。
- (4):根據(jù)任務(wù)狀態(tài)分類處理。
- (5):如果任務(wù)是就緒狀態(tài)、延時(shí)狀態(tài)、掛起狀態(tài)或者是在延時(shí)中被掛起, 這些任務(wù)狀態(tài)均與等待內(nèi)核對象是無關(guān)的,在內(nèi)核對象被刪除的時(shí)候無需進(jìn)行任何操作。
- (6):如果任務(wù)是無期限等待狀態(tài)或者是有期限等待狀態(tài), 那么在內(nèi)核對象被刪除的時(shí)候需要將這些任務(wù)恢復(fù)。如果這些任務(wù)在等待多個(gè)內(nèi)核對象(信號量或消息隊(duì)列等), 那么就需要強(qiáng)制解除任務(wù)對某一對象的等待,比如現(xiàn)在刪除的是消息隊(duì)列, 那么就將該任務(wù)對消息隊(duì)列的等待進(jìn)行解除。
- (7):如果啟用了任務(wù)隊(duì)列或消息隊(duì)列,清除(復(fù)位)任務(wù)的消息指針,任務(wù)等待的消息大小為0。
- (8):保存等待被中止時(shí)的時(shí)間戳到任務(wù)控制塊。
- (9):調(diào)用OS_PendListRemove()函數(shù)將任務(wù)從所有等待列表中移除。
- (10):調(diào)用OS_TaskRdy()函數(shù)讓任務(wù)進(jìn)入就緒態(tài)參與系統(tǒng)調(diào)度,準(zhǔn)備運(yùn)行。
- (11):修改任務(wù)狀態(tài)為就緒狀態(tài)。
- (12):標(biāo)記任務(wù)的等待對象被刪除。
- (13):標(biāo)記任務(wù)目前沒有等待任何對象。
- (14):如果任務(wù)在無期限等待中被掛起或者在有期限等待中被掛起, 也是需要將這些等待內(nèi)核對象的任務(wù)從等待中移除,但是由于在等待中被掛起,那么就不會將這些任務(wù)恢復(fù)為就緒態(tài), 僅僅是將任務(wù)從等待列表中移除。如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列,同樣也是講任務(wù)從等待的對象中移除即可。
- (15):如果啟用了任務(wù)隊(duì)列或消息隊(duì)列。
- (16):需要清除(復(fù)位)任務(wù)的消息指針,任務(wù)等待的消息大小為0。
- (17):保存等待被中止時(shí)的時(shí)間戳到任務(wù)控制塊。
- (18):調(diào)用OS_TickListRemove()函數(shù)讓任務(wù)脫離節(jié)拍列表。
- (19):調(diào)用OS_PendListRemove()函數(shù)將任務(wù)從所有等待列表中移除。
- (20):修改任務(wù)狀態(tài)為掛起狀態(tài),因?yàn)樵诘却斜粧炱?#xff0c;此時(shí)即使任務(wù)不等的內(nèi)核對象了,它還是處于掛起態(tài)。
- (21):任務(wù)的等待對象被刪除,標(biāo)記任務(wù)目前沒有等待任何對象。
- (22):如果任務(wù)狀態(tài)超出預(yù)期,不需處理,直接跳出。
消息隊(duì)列刪除函數(shù)OSQDel()的使用也是很簡單的,只需要傳入要刪除的消息隊(duì)列的句柄與選項(xiàng)還有保存返回的錯(cuò)誤類型即可。
調(diào)用函數(shù)時(shí), 系統(tǒng)將刪除這個(gè)消息隊(duì)列。
需要注意的是在調(diào)用刪除消息隊(duì)列函數(shù)前,系統(tǒng)應(yīng)存在已創(chuàng)建的消息隊(duì)列。
如果刪除消息隊(duì)列時(shí), 有任務(wù)正在等待消息,則不應(yīng)該進(jìn)行刪除操作,刪除之后的消息隊(duì)列就不可用了。
刪除消息隊(duì)列函數(shù)OSQDel()的使用實(shí)例具體如下:
OS_Q queue; //聲明消息隊(duì)列OS_ERR err;/* 刪除消息隊(duì)列 queue */
OSQDel ((OS_Q *)&queue, //指向消息隊(duì)列的指針
OS_OPT_DEL_NO_PEND,
(OS_ERR *)&err); //返回錯(cuò)誤類型
3、消息隊(duì)列發(fā)送函數(shù)OSQPost()
1. OSQPost()函數(shù)
任務(wù)或者中斷服務(wù)程序都可以給消息隊(duì)列發(fā)送消息,當(dāng)發(fā)送消息時(shí),如果隊(duì)列未滿,就說明運(yùn)行信息入隊(duì)。
μC/OS會從消息池中取出一個(gè)消息, 掛載到消息隊(duì)列的末尾(FIFO發(fā)送方式)。
如果是LIFO發(fā)送方式,則將消息掛載到消息隊(duì)列的頭部, 然后將消息中MsgPtr成員變量指向要發(fā)送的消息(此處可以理解為添加要發(fā)送的信息到消息(塊)中)。
如果系統(tǒng)有任務(wù)阻塞在消息隊(duì)列中,那么在發(fā)送了消息隊(duì)列的時(shí)候,會將任務(wù)解除阻塞,其源碼具體如下:
void OSQPost (OS_Q *p_q, //(1) //消息隊(duì)列指針
void *p_void, //(2) //消息指針OS_MSG_SIZE msg_size,//(3) //消息大小(單位:字節(jié))OS_OPT opt, //(4) //選項(xiàng)OS_ERR *p_err) //(5) //返回錯(cuò)誤類型
{CPU_TS ts;#ifdef OS_SAFETY_CRITICAL//(6)//如果啟用(默認(rèn)禁用)了安全檢測if (p_err == (OS_ERR *)0) { //如果錯(cuò)誤類型實(shí)參為空OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return; //返回,停止執(zhí)行}
#endif#if OS_CFG_ARG_CHK_EN > 0u//(7)//如果啟用了參數(shù)檢測if (p_q == (OS_Q *)0) { //如果 p_q 為空*p_err = OS_ERR_OBJ_PTR_NULL; //錯(cuò)誤類型為“內(nèi)核對象為空”return; //返回,停止執(zhí)行}switch (opt) { //(8)//根據(jù)選項(xiàng)分類處理case OS_OPT_POST_FIFO: //如果選項(xiàng)在預(yù)期內(nèi)case OS_OPT_POST_LIFO:case OS_OPT_POST_FIFO | OS_OPT_POST_ALL:case OS_OPT_POST_LIFO | OS_OPT_POST_ALL:case OS_OPT_POST_FIFO | OS_OPT_POST_NO_SCHED:case OS_OPT_POST_LIFO | OS_OPT_POST_NO_SCHED:case OS_OPT_POST_FIFO | OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED:case OS_OPT_POST_LIFO | OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED:break; //直接跳出default: //(9)//如果選項(xiàng)超出預(yù)期*p_err = OS_ERR_OPT_INVALID; //錯(cuò)誤類型為“選項(xiàng)非法”return; //返回,停止執(zhí)行}
#endif#if OS_CFG_OBJ_TYPE_CHK_EN > 0u//(10)//如果啟用了對象類型檢測if (p_q->Type != OS_OBJ_TYPE_Q) { //如果 p_q 不是消息隊(duì)列類型*p_err = OS_ERR_OBJ_TYPE; //錯(cuò)誤類型為“對象類型錯(cuò)誤”return; //返回,停止執(zhí)行}
#endifts = OS_TS_GET(); //獲取時(shí)間戳#if OS_CFG_ISR_POST_DEFERRED_EN > 0u//(11)//如果啟用了中斷延遲發(fā)布if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果該函數(shù)在中斷中被調(diào)用OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_Q, //將該消息發(fā)布到中斷消息隊(duì)列(void *)p_q,(void *)p_void,(OS_MSG_SIZE)msg_size,(OS_FLAGS )0,(OS_OPT )opt,(CPU_TS )ts,(OS_ERR *)p_err);return; //返回(尚未發(fā)布),停止執(zhí)行}
#endifOS_QPost(p_q, //將消息按照普通方式p_void,msg_size,opt,ts,p_err); //(12)
}
- (1):消息隊(duì)列指針,指向要發(fā)送消息的隊(duì)列。
- (2):消息指針,指向任何類型的消息數(shù)據(jù)。
- (3):消息的大小(單位:字節(jié))。
- (4):發(fā)送消息的選項(xiàng),在os.h中定義
#define OS_OPT_POST_FIFO (OS_OPT)(0x0000u)/* 默認(rèn)采用FIFO方式發(fā)送 */
#define OS_OPT_POST_LIFO (OS_OPT)(0x0010u)/*采用LIFO方式發(fā)送消息*/
#define OS_OPT_POST_1 (OS_OPT)(0x0000u)/*將消息發(fā)布到最高優(yōu)先級的等待任務(wù)*/
#define OS_OPT_POST_ALL (OS_OPT)(0x0200u)/*向所有等待的任務(wù)廣播消息*/#define OS_OPT_POST_NO_SCHED (OS_OPT)(0x8000u)/*發(fā)送消息但是不進(jìn)行任務(wù)調(diào)度*/
- (5):保存返回的錯(cuò)誤類型,用戶可以根據(jù)此變量得知錯(cuò)誤的原因。
- (6):如果啟用(默認(rèn)禁用)了安全檢測,在編譯時(shí)則會包含安全檢測相關(guān)的代碼,如果錯(cuò)誤類型實(shí)參為空, 系統(tǒng)會執(zhí)行安全檢測異常函數(shù),然后返回,停止執(zhí)行。
- (7):如果啟用了參數(shù)檢測,在編譯時(shí)則會包含參數(shù)檢測相關(guān)的代碼,如果 p_q 參數(shù)為空, 返回錯(cuò)誤類型為“內(nèi)核對象為空”的錯(cuò)誤代碼,并且退出,不執(zhí)行發(fā)送消息操作。
- (8):根據(jù)opt選項(xiàng)進(jìn)行分類處理,如果選項(xiàng)在預(yù)期內(nèi),直接退出,其實(shí)在這里只是對選項(xiàng)的一個(gè)檢查, 看看傳入的選項(xiàng)參數(shù)是否正確。
- (9):如果opt選項(xiàng)超出預(yù)期,返回錯(cuò)誤類型為“選項(xiàng)非法”的錯(cuò)誤代碼,并且退出,不執(zhí)行發(fā)送消息操作。
- (10):如果啟用了對象類型檢測,在編譯時(shí)則會包含對象類型檢測相關(guān)代碼, 如果 p_q 不是消息隊(duì)列類型,那么返回錯(cuò)誤類型為“對象類型有誤”的錯(cuò)誤代碼,并且退出,不執(zhí)行發(fā)送消息操作。
- (11):如果啟用了中斷延遲發(fā)布,并且發(fā)送消息的函數(shù)是在中斷中被調(diào)用, 此時(shí)就不該立即發(fā)送消息,而是將消息的發(fā)送放在指定發(fā)布任務(wù)中,此時(shí)系統(tǒng)就將消息發(fā)布到租單消息隊(duì)列中, 等待到中斷發(fā)布任務(wù)喚醒再發(fā)送消息,該函數(shù)會在中斷管理章節(jié)詳細(xì)講解。
- (12):而如果不是在中斷中調(diào)用OSQPost()函數(shù),或者未啟用中斷延遲發(fā)布, 則直接調(diào)用OS_QPost()函數(shù)進(jìn)行消息的發(fā)送
2. OS_QPost()函數(shù)
OS_QPost()函數(shù)源碼具體如下:
void OS_QPost (OS_Q *p_q, //消息隊(duì)列指針void *p_void, //消息指針OS_MSG_SIZE msg_size, //消息大小(單位:字節(jié))OS_OPT opt, //選項(xiàng)CPU_TS ts, //消息被發(fā)布時(shí)的時(shí)間戳OS_ERR *p_err) //返回錯(cuò)誤類型
{OS_OBJ_QTY cnt;OS_OPT post_type;OS_PEND_LIST *p_pend_list;OS_PEND_DATA *p_pend_data;OS_PEND_DATA *p_pend_data_next;OS_TCB *p_tcb;CPU_SR_ALLOC(); //使用到臨界段(在關(guān)/開中斷時(shí))時(shí)必須用到該宏,該宏聲明和//定義一個(gè)局部變量,用于保存關(guān)中斷前的 CPU 狀態(tài)寄存器// SR(臨界段關(guān)中斷只需保存SR),開中斷時(shí)將該值還原。OS_CRITICAL_ENTER(); //進(jìn)入臨界段p_pend_list = &p_q->PendList; //取出該隊(duì)列的等待列表if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) //(1)//如果沒有任務(wù)在等待該隊(duì)列{if ((opt & OS_OPT_POST_LIFO) == (OS_OPT)0) //把消息發(fā)布到隊(duì)列的末端{post_type = OS_OPT_POST_FIFO; //(2)}else//把消息發(fā)布到隊(duì)列的前端{post_type = OS_OPT_POST_LIFO; //(3)}OS_MsgQPut(&p_q->MsgQ, //把消息放入消息隊(duì)列p_void,msg_size,post_type,ts,p_err); //(4)OS_CRITICAL_EXIT(); //退出臨界段return; //返回,執(zhí)行完畢}/* 如果有任務(wù)在等待該隊(duì)列 */if ((opt & OS_OPT_POST_ALL) != (OS_OPT)0) //(5)//如果要把消息發(fā)布給所有等待任務(wù){cnt = p_pend_list->NbrEntries; //獲取等待任務(wù)數(shù)目}else//如果要把消息發(fā)布給一個(gè)等待任務(wù){cnt = (OS_OBJ_QTY)1; //(6)//要處理的任務(wù)數(shù)目為1}p_pend_data = p_pend_list->HeadPtr; //(7)//獲取等待列表的頭部(任務(wù))while (cnt > 0u) //(8)//根據(jù)要發(fā)布的任務(wù)數(shù)目逐個(gè)發(fā)布{p_tcb = p_pend_data->TCBPtr; //(9)p_pend_data_next = p_pend_data->NextPtr;OS_Post((OS_PEND_OBJ *)((void *)p_q), //把消息發(fā)布給任務(wù)p_tcb,p_void,msg_size,ts); //(10)p_pend_data = p_pend_data_next;cnt--; //(11)}OS_CRITICAL_EXIT_NO_SCHED(); //退出臨界段(無調(diào)度)if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) //如果沒選擇“發(fā)布完不調(diào)度任務(wù)”{OSSched(); //(12)//任務(wù)調(diào)度}*p_err = OS_ERR_NONE; //錯(cuò)誤類型為“無錯(cuò)誤”
}
- (1):使用局部變量p_pend_list獲取隊(duì)列的等待列表, 然后查看等待列表中是否有任務(wù)在等待,分情況處理,因?yàn)闆]有任務(wù)等待就直接將消息放入隊(duì)列中即可, 而有任務(wù)在等待則有可能需要喚醒該任務(wù)。
- (2):如果沒有任務(wù)在等待,系統(tǒng)就會看看用戶發(fā)送消息的選項(xiàng)是什么, 如果是發(fā)送到細(xì)細(xì)道來的末端(隊(duì)尾,FIFO方式),那么表示發(fā)送類型的post_type變量就被設(shè)置為OS_OPT_POST_FIFO。
- (3):否則就設(shè)置為OS_OPT_POST_LIFO, 采用LIFO方式發(fā)送消息。將消息發(fā)送到隊(duì)列的前端(對頭)。
- (4):調(diào)用OS_MsgQPut()函數(shù)將消息放入隊(duì)列中, 執(zhí)行完畢就退出
- (5):而如果有任務(wù)在等待消息,會有兩種情況, 一種是將消息發(fā)送到所有等待任務(wù)(廣播消息),另一種是只將消息發(fā)送到等待任務(wù)中最高優(yōu)先級的任務(wù)。 根據(jù)opt選項(xiàng)選擇其中一種方式進(jìn)行發(fā)送消息,如果要把消息發(fā)送給所有等待任務(wù),那就首先獲取到等待任務(wù)個(gè)數(shù), 保存在要處理任務(wù)個(gè)數(shù)cnt變量中。
- (6):否則就是把消息發(fā)布給一個(gè)等待任務(wù),要處理任務(wù)個(gè)數(shù)cnt變量為1。
- (7):獲取等待列表中的第一個(gè)任務(wù)。
- (8):根據(jù)要處理任務(wù)個(gè)數(shù)cnt逐個(gè)將消息發(fā)送出去。
- (9):獲取任務(wù)的控制塊。
- (10):調(diào)用OS_Post()函數(shù)把消息發(fā)送給任務(wù)
- (11):每處理完一個(gè)任務(wù),cnt變量就要減一,等到為0的時(shí)候退出while循環(huán)。
- (12):如果沒選擇“發(fā)送完不調(diào)度任務(wù)”,在發(fā)送消息完成的時(shí)候就要進(jìn)行一次任務(wù)調(diào)度。
3. OS_MsgQPut()函數(shù)
OS_MsgQPut()源碼如下:
void OS_MsgQPut (OS_MSG_Q *p_msg_q, //消息隊(duì)列指針void *p_void, //消息指針OS_MSG_SIZE msg_size, //消息大小(單位:字節(jié))OS_OPT opt, //選項(xiàng)CPU_TS ts, //消息被發(fā)布時(shí)的時(shí)間戳OS_ERR *p_err) //返回錯(cuò)誤類型
{OS_MSG *p_msg;OS_MSG *p_msg_in;#ifdef OS_SAFETY_CRITICAL//如果啟用了安全檢測if (p_err == (OS_ERR *)0) //如果錯(cuò)誤類型實(shí)參為空{OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return; //返回,停止執(zhí)行}
#endifif (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) //如果消息隊(duì)列已沒有可用空間{*p_err = OS_ERR_Q_MAX; //錯(cuò)誤類型為“隊(duì)列已滿”return; //返回,停止執(zhí)行}if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) //如果消息池沒有可用消息{*p_err = OS_ERR_MSG_POOL_EMPTY; //錯(cuò)誤類型為“消息池沒有消息”return; //返回,停止執(zhí)行}/* 從消息池獲取一個(gè)消息(暫存于 p_msg )*/p_msg = OSMsgPool.NextPtr; //(1)//將消息控制塊從消息池移除OSMsgPool.NextPtr = p_msg->NextPtr; //(2)//指向下一個(gè)消息(取走首個(gè)消息)OSMsgPool.NbrFree--; //(3)//消息池可用消息數(shù)減1OSMsgPool.NbrUsed++; //(4)//消息池被用消息數(shù)加1if (OSMsgPool.NbrUsedMax < OSMsgPool.NbrUsed) //(5)//更新消息被用最大數(shù)目的歷史記錄{OSMsgPool.NbrUsedMax = OSMsgPool.NbrUsed;}/* 將獲取的消息插入消息隊(duì)列 */if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) //(6)//如果消息隊(duì)列目前沒有消息{p_msg_q->InPtr = p_msg; //將其入隊(duì)指針指向該消息p_msg_q->OutPtr = p_msg; //出隊(duì)指針也指向該消息p_msg_q->NbrEntries = (OS_MSG_QTY)1; //隊(duì)列的消息數(shù)為1p_msg->NextPtr = (OS_MSG *)0; //該消息的下一個(gè)消息為空}else//(7)//如果消息隊(duì)列目前已有消息{if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) //如果用FIFO方式插入隊(duì)列,{p_msg_in = p_msg_q->InPtr;//將消息插入入隊(duì)端,入隊(duì)p_msg_in->NextPtr = p_msg; //指針指向該消息。p_msg_q->InPtr = p_msg;p_msg->NextPtr = (OS_MSG *)0;}else//(8)//如果用LIFO方式插入隊(duì)列,{p_msg->NextPtr = p_msg_q->OutPtr; //將消息插入出隊(duì)端,出隊(duì)p_msg_q->OutPtr = p_msg; //指針指向該消息。}p_msg_q->NbrEntries++; //(9)//消息隊(duì)列的消息數(shù)目加1}if (p_msg_q->NbrEntriesMax < p_msg_q->NbrEntries) //(10)//更新改消息隊(duì)列的最大消息{p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries; //數(shù)目的歷史記錄。}p_msg->MsgPtr = p_void; //(11)//給該消息填寫消息內(nèi)容p_msg->MsgSize = msg_size; //(12)//給該消息填寫消息大小p_msg->MsgTS = ts; //(13)//填寫發(fā)布該消息時(shí)的時(shí)間戳*p_err = OS_ERR_NONE; // (14)//錯(cuò)誤類型為“無錯(cuò)誤”
}
- (1):從消息池獲取一個(gè)消息(暫存于 p_msg ), OSMsgPool是消息池,它的NextPtr成員變量指向消息池中可用的消息。
- (2):更新消息池中NextPtr成員變量,指向消息池中下一個(gè)可用的消息。
- (3):消息池可中用消息個(gè)數(shù)減1。
- (4):消息池已使用的消息個(gè)數(shù)加1。
- (5):更新消息被用最大數(shù)目的歷史記錄。
- (6):將獲取的消息插入消息隊(duì)列,插入隊(duì)列時(shí)分兩種情況:一種是隊(duì)列中有消息情況, 另一種是隊(duì)列中沒有消息情況。如果消息隊(duì)列目前沒有消息,將隊(duì)列中的入隊(duì)指針指向該消息,出隊(duì)指針也指向該消息, 因?yàn)楝F(xiàn)在消息放進(jìn)來了,只有一個(gè)消息,無論是入隊(duì)還是出隊(duì),都是該消息,更新隊(duì)列的消息個(gè)數(shù)為1,該消息的下一個(gè)消息為空。
- (7):而如果消息隊(duì)列目前已有消息,那么又分兩種入隊(duì)的選項(xiàng), 是先進(jìn)先出排隊(duì)呢還是后進(jìn)先出排隊(duì)呢?如果采用FIFO方式插入隊(duì)列,那么就將消息插入入隊(duì)端, 消息隊(duì)列的最后一個(gè)消息的NextPtr指針就指向該消息,然后入隊(duì)的消息成為隊(duì)列中排隊(duì)的最后一個(gè)消息, 那么需要更新它的下一個(gè)消息為空。
- (8):而如果采用LIFO方式插入隊(duì)列, 將消息插入出隊(duì)端,隊(duì)列中出隊(duì)指針OutPtr指向該消息,需要出隊(duì)的時(shí)候就是 該消息首先出隊(duì),這就是后進(jìn)先出原則。
- (9):無論是采用哪種方式入隊(duì),消息隊(duì)列的消息數(shù)目都要加1。
- (10):更新改消息隊(duì)列的最大消息。
- (11):既然消息已經(jīng)入隊(duì)了,那肯定得添加我們自己的消息內(nèi)容啊, 需要給該消息填寫消息內(nèi)容,消息中的MsgPtr指針指向我們的消息內(nèi)容。
- (12):給該消息填寫我們發(fā)送的消息大小。
- (13):填寫發(fā)布該消息時(shí)的時(shí)間戳。
- (14):當(dāng)程序執(zhí)行到這里,表面就是沒有錯(cuò)誤,返回錯(cuò)誤類型為“無錯(cuò)誤”的錯(cuò)誤代碼。
4. OS_Post()函數(shù)
OS_Post()源碼如下:
void OS_Post (OS_PEND_OBJ *p_obj, //(1) //內(nèi)核對象類型指針OS_TCB *p_tcb, //(2) //任務(wù)控制塊void *p_void, //(3) //消息OS_MSG_SIZE msg_size, //(4) //消息大小CPU_TS ts) //(5) //時(shí)間戳
{switch (p_tcb->TaskState) //(6)//根據(jù)任務(wù)狀態(tài)分類處理{case OS_TASK_STATE_RDY: //如果任務(wù)處于就緒狀態(tài)case OS_TASK_STATE_DLY: //如果任務(wù)處于延時(shí)狀態(tài)case OS_TASK_STATE_SUSPENDED: //如果任務(wù)處于掛起狀態(tài)case OS_TASK_STATE_DLY_SUSPENDED://如果任務(wù)處于延時(shí)中被掛起狀態(tài)break; //(7)//不用處理,直接跳出case OS_TASK_STATE_PEND: //如果任務(wù)處于無期限等待狀態(tài)case OS_TASK_STATE_PEND_TIMEOUT: //如果任務(wù)處于有期限等待狀態(tài)if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) //(8)//如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列{OS_Post1(p_obj, //標(biāo)記哪個(gè)內(nèi)核對象被發(fā)布p_tcb,p_void,msg_size,ts); //(9)}else //(10)//如果任務(wù)不是在等待多個(gè)信號量或消息隊(duì)列{
#if (OS_MSG_EN > 0u)
//如果啟用了任務(wù)隊(duì)列或消息隊(duì)列p_tcb->MsgPtr = p_void; //(11)//保存消息到等待任務(wù)p_tcb->MsgSize = msg_size;
#endifp_tcb->TS = ts; //(12)//保存時(shí)間戳到等待任務(wù)}if (p_obj != (OS_PEND_OBJ *)0) //如果內(nèi)核對象不為空{OS_PendListRemove(p_tcb); //(13)//從等待列表移除該等待任務(wù)
#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_PendDbgNameRemove(p_obj, //移除內(nèi)核對象的調(diào)試名p_tcb);
#endif}OS_TaskRdy(p_tcb); //(14) //讓該等待任務(wù)準(zhǔn)備運(yùn)行p_tcb->TaskState = OS_TASK_STATE_RDY; //(15)//任務(wù)狀態(tài)改為就緒狀態(tài)p_tcb->PendStatus = OS_STATUS_PEND_OK; //(16)//清除等待狀態(tài)p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //(17)//標(biāo)記不再等待
break;case OS_TASK_STATE_PEND_SUSPENDED://如果任務(wù)在無期限等待中被掛起case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED://如果任務(wù)在有期限等待中被掛起if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) //(18)//如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列{OS_Post1(p_obj, //標(biāo)記哪個(gè)內(nèi)核對象被發(fā)布p_tcb,p_void,msg_size,ts); //(19)}else //(20)//如果任務(wù)不在等待多個(gè)信號量或消息隊(duì)列{
#if (OS_MSG_EN > 0u)//如果啟用了調(diào)試代碼和變量p_tcb->MsgPtr = p_void; //(21)//保存消息到等待任務(wù)p_tcb->MsgSize = msg_size;
#endifp_tcb->TS = ts; //保存時(shí)間戳到等待任務(wù)}OS_TickListRemove(p_tcb); //(22)//從節(jié)拍列表移除該等待任務(wù)if (p_obj != (OS_PEND_OBJ *)0) //如果內(nèi)核對象為空{OS_PendListRemove(p_tcb); //(23)//從等待列表移除該等待任務(wù)
#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_PendDbgNameRemove(p_obj, //移除內(nèi)核對象的調(diào)試名p_tcb);
#endif}p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; //(24)//任務(wù)狀態(tài)改為被掛起狀態(tài)p_tcb->PendStatus = OS_STATUS_PEND_OK; //(25)//清除等待狀態(tài)p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; //(26)//標(biāo)記不再等待break;default: //(27)//如果任務(wù)狀態(tài)超出預(yù)期break; //直接跳出}
}
- (1):內(nèi)核對象類型指針,表示是哪個(gè)內(nèi)核對象進(jìn)行發(fā)布(釋放/發(fā)送)操作。
- (2):任務(wù)控制塊指針,指向被操作的任務(wù)。
- (3):消息指針。
- (4):消息大小。
- (5):時(shí)間戳。
- (6):根據(jù)任務(wù)狀態(tài)分類處理。
- (7):如果任務(wù)處于就緒狀態(tài)、延時(shí)狀態(tài)、掛起狀態(tài)或者是延時(shí)中被掛起狀態(tài),都不用處理, 直接退出,因?yàn)楝F(xiàn)在這個(gè)操作是內(nèi)核對象進(jìn)行發(fā)布(釋放)操作,而這些狀態(tài)的任務(wù)是與內(nèi)核對象無關(guān)的狀態(tài),
也就是這些任務(wù)沒在等待相關(guān)的內(nèi)核對象(如消息隊(duì)列、信號量等)。 - (8):如果任務(wù)處于無期限等待狀態(tài)或者是有期限等待狀態(tài),那么就需要處理了,先看看任務(wù)是不是在等待多個(gè)內(nèi)核對象。
- (9):如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列, 就調(diào)用OS_Post1()函數(shù)標(biāo)記一下是哪個(gè)內(nèi)核對象進(jìn)行發(fā)布(釋放)操作。
- (10):如果任務(wù)不是在等待多個(gè)信號量或消息隊(duì)列,就直接操作即可。
- (11):如果啟用了任務(wù)隊(duì)列或消息隊(duì)列(啟用了OS_MSG_EN宏定義), 保存消息到等待任務(wù)控制塊的MsgPtr成員變量中, 將消息的大小保存到等待任務(wù)控制塊的MsgSize成員變量中。
- (12):保存時(shí)間戳到等待任務(wù)控制塊的TS成員變量中。
- (13):如果內(nèi)核對象不為空,調(diào)用OS_PendListRemove()函數(shù)從等待列表移除該等待任務(wù)。
- (14):調(diào)用OS_TaskRdy()函數(shù)讓該等待任務(wù)準(zhǔn)備運(yùn)行。
- (15):任務(wù)狀態(tài)改為就緒狀態(tài)。
- (16):清除任務(wù)的等待狀態(tài)。
- (17):標(biāo)記任務(wù)不再等待。
- (18):如果任務(wù)在無期限等待中被掛起,或者任務(wù)在有期限等待中被掛起,反正任務(wù)就是在等待中被掛起了, 也能進(jìn)行內(nèi)核對象發(fā)布(釋放)操作,同理,先看看任務(wù)是不是在等待多個(gè)內(nèi)核對象。
- (19):如果任務(wù)在等待多個(gè)信號量或消息隊(duì)列, 就調(diào)用OS_Post1()函數(shù)標(biāo)記一下是哪個(gè)內(nèi)核對象進(jìn)行發(fā)布(釋放)操作。
- (20):如果任務(wù)不在等待多個(gè)信號量或消息隊(duì)列,就直接操作即可。
- (21):如果啟用了任務(wù)隊(duì)列或消息隊(duì)列(啟用了OS_MSG_EN宏定義), 保存消息到等待任務(wù)控制塊的MsgPtr成員變量中,將消息的大小保存到等待任務(wù)控制塊的MsgSize成員變量中。
- (22):調(diào)用OS_TickListRemove()函數(shù)將任務(wù)從節(jié)拍列表中移除。
- (23):從等待列表移除該等待任務(wù)。
- (24):任務(wù)狀態(tài)改為被掛起狀態(tài)。
- (25):清除任務(wù)的等待狀態(tài)。
- (26):標(biāo)記任務(wù)不再等待。
- (27):如果任務(wù)狀態(tài)超出預(yù)期,直接跳出。
從消息隊(duì)列的入隊(duì)操作(發(fā)送消息)我們可以看出:
μC/OS支持向所有任務(wù)發(fā)送消息,也支持只向一個(gè)任務(wù)發(fā)送消息, 這樣子系統(tǒng)的靈活性就會大大提高,與此同時(shí),μC/OS還支持中斷延遲發(fā)布,不在中斷中直接發(fā)送消息。
消息隊(duì)列的發(fā)送函數(shù)OSQPost()使用實(shí)例具體如下:
/* 發(fā)送消息到消息隊(duì)列 queue */
OSQPost ((OS_Q *)&queue, //消息變量指針(void *)"Binghuo μC/OS-III",//要發(fā)送的數(shù)據(jù)的指針,將內(nèi)存塊首地址通過隊(duì)列“發(fā)送出去”(OS_MSG_SIZE )sizeof ( "Binghuo μC/OS-III" ), //數(shù)據(jù)字節(jié)大小(OS_OPT )OS_OPT_POST_FIFO | OS_OPT_POST_ALL,//先進(jìn)先出和發(fā)布給全部任務(wù)的形式(OS_ERR *)&err);
4、消息隊(duì)列獲取函數(shù)OSQPend()
當(dāng)任務(wù)試圖從隊(duì)列中的獲取消息時(shí),用戶可以指定一個(gè)阻塞超時(shí)時(shí)間,當(dāng)且僅當(dāng)消息隊(duì)列中有消息的時(shí)候,任務(wù)才能獲取到消息。
在這段時(shí)間中,如果隊(duì)列為空,該任務(wù)將保持阻塞狀態(tài)以等待隊(duì)列消息有效。
當(dāng)其他任務(wù)或中斷服務(wù)程序往其等待的隊(duì)列中寫入了數(shù)據(jù), 該任務(wù)將自動由阻塞態(tài)轉(zhuǎn)為就緒態(tài)。
當(dāng)任務(wù)等待的時(shí)間超過了用戶指定的阻塞時(shí)間,即使隊(duì)列中尚無有效消息, 任務(wù)也會自動從阻塞態(tài)轉(zhuǎn)為就緒態(tài)。
1. OSQPend()函數(shù)
OSQPend()函數(shù)源碼具體如下:
void *OSQPend (OS_Q *p_q, //(1) //消息隊(duì)列指針OS_TICK timeout, //(2) //等待期限(單位:時(shí)鐘節(jié)拍)OS_OPT opt, //(3) //選項(xiàng)OS_MSG_SIZE *p_msg_size,//(4) //返回消息大小(單位:字節(jié))CPU_TS *p_ts, //(5) //獲取等到消息時(shí)的時(shí)間戳OS_ERR *p_err) //(6) //返回錯(cuò)誤類型
{OS_PEND_DATA pend_data;void *p_void;CPU_SR_ALLOC(); //使用到臨界段(在關(guān)/開中斷時(shí))時(shí)必須用到該宏,該宏聲明和//定義一個(gè)局部變量,用于保存關(guān)中斷前的 CPU 狀態(tài)寄存器// SR(臨界段關(guān)中斷只需保存SR),開中斷時(shí)將該值還原。#ifdef OS_SAFETY_CRITICAL//(7)//如果啟用(默認(rèn)禁用)了安全檢測if (p_err == (OS_ERR *)0) //如果錯(cuò)誤類型實(shí)參為空{OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u//(8)//如果啟用了中斷中非法調(diào)用檢測if (OSIntNestingCtr > (OS_NESTING_CTR)0) //如果該函數(shù)在中斷中被調(diào)用{*p_err = OS_ERR_PEND_ISR; //錯(cuò)誤類型為“在中斷中中止等待”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_ARG_CHK_EN > 0u//(9)//如果啟用了參數(shù)檢測if (p_q == (OS_Q *)0) //如果 p_q 為空{*p_err = OS_ERR_OBJ_PTR_NULL; //錯(cuò)誤類型為“對象為空”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}if (p_msg_size == (OS_MSG_SIZE *)0) //如果 p_msg_size 為空{*p_err = OS_ERR_PTR_INVALID; //錯(cuò)誤類型為“指針不可用”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}switch (opt) //(10)//根據(jù)選項(xiàng)分類處理{case OS_OPT_PEND_BLOCKING: //如果選項(xiàng)在預(yù)期內(nèi)case OS_OPT_PEND_NON_BLOCKING:break; //直接跳出default: //(11)//如果選項(xiàng)超出預(yù)期*p_err = OS_ERR_OPT_INVALID; //返回錯(cuò)誤類型為“選項(xiàng)非法”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endif#if OS_CFG_OBJ_TYPE_CHK_EN > 0u//(12)//如果啟用了對象類型檢測if (p_q->Type != OS_OBJ_TYPE_Q) //如果 p_q 不是消息隊(duì)列類型{*p_err = OS_ERR_OBJ_TYPE; //錯(cuò)誤類型為“對象類型有誤”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}
#endifif (p_ts != (CPU_TS *)0) //(13) //如果 p_ts 非空{*p_ts = (CPU_TS )0; //初始化(清零)p_ts,待用于返回時(shí)間戳}CPU_CRITICAL_ENTER(); //關(guān)中斷p_void = OS_MsgQGet(&p_q->MsgQ, //(14)//從消息隊(duì)列獲取一個(gè)消息p_msg_size,p_ts,p_err);if (*p_err == OS_ERR_NONE) //(15)//如果獲取消息成功{CPU_CRITICAL_EXIT(); //開中斷return (p_void); //返回消息內(nèi)容}/* 如果獲取消息不成功 */ //(16)if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) //如果選擇了不阻塞任務(wù){CPU_CRITICAL_EXIT(); //開中斷*p_err = OS_ERR_PEND_WOULD_BLOCK; //錯(cuò)誤類型為“等待渴求阻塞”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}else//(17)//如果選擇了阻塞任務(wù){if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0)//(18)//如果調(diào)度器被鎖{CPU_CRITICAL_EXIT(); //開中斷*p_err = OS_ERR_SCHED_LOCKED; //錯(cuò)誤類型為“調(diào)度器被鎖”return ((void *)0); //返回0(有錯(cuò)誤),停止執(zhí)行}}/* 如果調(diào)度器未被鎖 */OS_CRITICAL_ENTER_CPU_EXIT(); //(19)//鎖調(diào)度器,重開中斷OS_Pend(&pend_data,//阻塞當(dāng)前任務(wù),等待消息隊(duì)列,(OS_PEND_OBJ *)((void *)p_q), //將當(dāng)前任務(wù)脫離就緒列表,并OS_TASK_PEND_ON_Q, //插入節(jié)拍列表和等待列表。timeout); //(20)OS_CRITICAL_EXIT_NO_SCHED(); //開調(diào)度器,但不進(jìn)行調(diào)度OSSched(); //(21)//找到并調(diào)度最高優(yōu)先級就緒任務(wù)/* 當(dāng)前任務(wù)(獲得消息隊(duì)列的消息)得以繼續(xù)運(yùn)行 */CPU_CRITICAL_ENTER(); // (22)//關(guān)中斷switch (OSTCBCurPtr->PendStatus) //(23)//根據(jù)當(dāng)前運(yùn)行任務(wù)的等待狀態(tài)分類處理{case OS_STATUS_PEND_OK: //(24)//如果等待狀態(tài)正常p_void = OSTCBCurPtr->MsgPtr; // (25)//從(發(fā)布時(shí)放于)任務(wù)控制塊提取消息*p_msg_size = OSTCBCurPtr->MsgSize; //提取消息大小if (p_ts != (CPU_TS *)0) //如果 p_ts 非空{*p_ts = OSTCBCurPtr->TS; //獲取任務(wù)等到消息時(shí)的時(shí)間戳}*p_err = OS_ERR_NONE; //錯(cuò)誤類型為“無錯(cuò)誤”break; //跳出case OS_STATUS_PEND_ABORT: //(26)//如果等待被中止p_void = (void *)0; //返回消息內(nèi)容為空*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小為0if (p_ts != (CPU_TS *)0) //如果 p_ts 非空{*p_ts = OSTCBCurPtr->TS; //獲取等待被中止時(shí)的時(shí)間戳}*p_err = OS_ERR_PEND_ABORT; //錯(cuò)誤類型為“等待被中止”break; //跳出case OS_STATUS_PEND_TIMEOUT: //(27)//如果等待超時(shí)p_void = (void *)0; //返回消息內(nèi)容為空*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小為0if (p_ts != (CPU_TS *)0) //如果 p_ts 非空{*p_ts = (CPU_TS )0; //清零 p_ts}*p_err = OS_ERR_TIMEOUT; //錯(cuò)誤類型為“等待超時(shí)”break; //跳出case OS_STATUS_PEND_DEL: //(28)//如果等待的內(nèi)核對象被刪除p_void = (void *)0; //返回消息內(nèi)容為空*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小為0if (p_ts != (CPU_TS *)0) //如果 p_ts 非空{*p_ts = OSTCBCurPtr->TS; //獲取對象被刪時(shí)的時(shí)間戳}*p_err = OS_ERR_OBJ_DEL; //錯(cuò)誤類型為“等待對象被刪”break; //跳出default: //(29)//如果等待狀態(tài)超出預(yù)期p_void = (void *)0; //返回消息內(nèi)容為空*p_msg_size = (OS_MSG_SIZE)0; //返回消息大小為0*p_err = OS_ERR_STATUS_INVALID; //錯(cuò)誤類型為“狀態(tài)非法”break; //跳出}CPU_CRITICAL_EXIT(); //開中斷
return(p_void); //(30)//返回消息內(nèi)容
}
- (1):消息隊(duì)列指針,指向要獲取消息的隊(duì)列。
- (2):指定阻塞時(shí)間(單位:時(shí)鐘節(jié)拍)。
- (3):獲取消息的選項(xiàng),在os.h中有定義。
- (4):用于保存返回獲取的消息大小(單位:字節(jié))。
- (5):用于保存返回等到消息時(shí)的時(shí)間戳。
- (6):用于保存返回的錯(cuò)誤類型,用戶可以根據(jù)此變量得知錯(cuò)誤的原因。
- (7):如果啟用(默認(rèn)禁用)了安全檢測,在編譯時(shí)則會包含安全檢測相關(guān)的代碼, 如果錯(cuò)誤類型實(shí)參為空,系統(tǒng)會執(zhí)行安全檢測異常函數(shù),然后返回,停止執(zhí)行。
- (8):如果啟用了中斷中非法調(diào)用檢測,并且如果該函數(shù)在中斷中被調(diào)用, 則返回錯(cuò)誤類型為“在中斷獲取消息”的錯(cuò)誤代碼,然后退出,停止執(zhí)行。
- (9):如果啟用了參數(shù)檢測,在編譯時(shí)則會包含參數(shù)檢測相關(guān)的代碼, 如果 p_q 參數(shù)為空,返回錯(cuò)誤類型為“內(nèi)核對象為空”的錯(cuò)誤代碼,并且退出,不執(zhí)行獲取消息操作。
- (10):根據(jù)opt選項(xiàng)進(jìn)行分類處理,如果選項(xiàng)在預(yù)期內(nèi),直接退出, 其實(shí)在這里只是對選項(xiàng)的一個(gè)檢查,看看傳入的選項(xiàng)參數(shù)是否正確。
- (11):如果opt選項(xiàng)超出預(yù)期, 返回錯(cuò)誤類型為“選項(xiàng)非法”的錯(cuò)誤代碼,并且退出,不執(zhí)行獲取消息操作。
- (12):如果啟用了對象類型檢測,在編譯時(shí)則會包含對象類型檢測相關(guān)代碼, 如果 p_q 不是消息隊(duì)列類型,那么返回錯(cuò)誤類型為“對象類型有誤”的錯(cuò)誤代碼,并且退出,不執(zhí)行獲取消息操作。
- (13):如果 p_ts 非空,就初始化(清零)p_ts,待用于返回時(shí)間戳。
- (14):調(diào)用OS_MsgQGet()函數(shù)從消息隊(duì)列獲取一個(gè)消息
- (15):如果獲取消息成功,就返回消息的內(nèi)容。
- (16):如果獲取消息不成功,并且用戶選擇了不阻塞等待, 則返回錯(cuò)誤類型為“等待渴求阻塞(OS_ERR_PEND_WOULD_BLOCK)”的錯(cuò)誤代碼,并且返回0,表示沒有獲取到消息。
- (17):當(dāng)獲取消息不成功的時(shí)候,用戶選擇了阻塞等待,那么就會將任務(wù)狀態(tài)變?yōu)樽枞麘B(tài)以等待消息。
- (18):判斷一下調(diào)度器是否被鎖,如果被鎖了,則返回錯(cuò)誤類型為“調(diào)度器被鎖”的錯(cuò)誤代碼,然后退出。
- (19):如果調(diào)度器未被鎖,就鎖定調(diào)度器,重新打開中斷。
為什么剛剛調(diào)度器被鎖就錯(cuò)誤的呢?而現(xiàn)在又要鎖定調(diào)度器?
那是因?yàn)橹版i定的調(diào)度器不是由這個(gè)函數(shù)進(jìn)行鎖定的, 這是不允許的,因?yàn)楝F(xiàn)在要阻塞當(dāng)前任務(wù),而調(diào)度器鎖定了就表示無法進(jìn)行任務(wù)調(diào)度,這也是不允許的。
那為什么又要關(guān)閉調(diào)度器呢, 因?yàn)榻酉聛淼牟僮魇切枰僮麝?duì)列與任務(wù)的列表,這個(gè)時(shí)間就不會很短,系統(tǒng)不希望有其他任務(wù)來操作任務(wù)列表。這樣可能引起其他任務(wù)解除阻塞, 這可能會發(fā)生優(yōu)先級翻轉(zhuǎn)。
比如任務(wù)A的優(yōu)先級低于當(dāng)前任務(wù),但是在當(dāng)前任務(wù)進(jìn)入阻塞的過程中,任務(wù)A卻因?yàn)槠渌蚪獬枞?#xff0c; 那系統(tǒng)肯定是會去運(yùn)行任務(wù)A,這顯然是要絕對禁止的。
掛起調(diào)度器意味著任務(wù)不能切換并且不準(zhǔn)調(diào)用可能引起任務(wù)切換的API函數(shù), 所以鎖定調(diào)度器、打開中斷這樣的處理,既不會影響中斷的響應(yīng),又避免了其他任務(wù)來操作隊(duì)列與任務(wù)的列表。 - (20):調(diào)用OS_Pend()函數(shù)將當(dāng)前任務(wù)脫離就緒列表, 并根據(jù)用戶指定的阻塞時(shí)間插入節(jié)拍列表和隊(duì)列等待列表, 然后打開調(diào)度器,但不進(jìn)行調(diào)度
- (21):在這里就進(jìn)行一次任務(wù)的調(diào)度。
- (22):程序能執(zhí)行到這里,就說明大體上有兩種情況,要么是消息隊(duì)列中有消息入隊(duì),任務(wù)獲取到消息了; 任務(wù)還沒獲取到消息(任務(wù)沒獲取到消息的情況有很多種),無論是哪種情況,都先把中斷關(guān)掉再說。
- (23):根據(jù)當(dāng)前運(yùn)行任務(wù)的等待狀態(tài)分類處理。
- (24):如果任務(wù)狀態(tài)是OS_STATUS_PEND_OK,則表示任務(wù)獲取到消息了。
- (25):從任務(wù)控制塊中提取消息,這是因?yàn)樵诎l(fā)送消息給任務(wù)的時(shí)候, 會將消息放入任務(wù)控制塊的MsgPtr成員變量中,然后繼續(xù)提取消息大小,如果p_ts非空,記錄獲取任務(wù)等到消息時(shí)的時(shí)間戳, 返回錯(cuò)誤類型為“無錯(cuò)誤”的錯(cuò)誤代碼,跳出switch語句。
- (26):如果任務(wù)在等待(阻塞)被中止,則返回消息內(nèi)容為空,返回消息大小為0, 如果p_ts非空,獲取等待被中止時(shí)的時(shí)間戳,返回錯(cuò)誤類型為“等待被中止”的錯(cuò)誤代碼,跳出switch語句。
- (27):如果等待(阻塞)超時(shí),說明等待的時(shí)間過去了,任務(wù)也沒獲取到消息, 則返回消息內(nèi)容為空,返回消息大小為0,如果p_ts非空,將p_ts清零,返回錯(cuò)誤類型為“等待超時(shí)”的錯(cuò)誤代碼,跳出switch語句。
- (28):如果等待的內(nèi)核對象被刪除,則返回消息內(nèi)容為空,返回消息大小為0, 如果p_ts非空,獲取對象被刪時(shí)的時(shí)間戳,返回錯(cuò)誤類型為“等待對象被刪”的錯(cuò)誤代碼,跳出switch語句。
- (29):如果等待狀態(tài)超出預(yù)期,則返回消息內(nèi)容為空,返回消息大小為0, 返回錯(cuò)誤類型為“狀態(tài)非法”的錯(cuò)誤代碼,跳出switch語句。
- (30):打開中斷,返回消息內(nèi)容。
2. OS_MsgQGet()函數(shù)
OS_MsgQGet()函數(shù)從消息隊(duì)列獲取一個(gè)消息,其源碼具體如下:
void *OS_MsgQGet (OS_MSG_Q *p_msg_q, //消息隊(duì)列OS_MSG_SIZE *p_msg_size, //返回消息大小CPU_TS *p_ts, //返回某些操作的時(shí)間戳OS_ERR *p_err) //返回錯(cuò)誤類型
{OS_MSG *p_msg;void *p_void;#ifdef OS_SAFETY_CRITICAL//如果啟用(默認(rèn)禁用)了安全檢測if (p_err == (OS_ERR *)0) //如果錯(cuò)誤類型實(shí)參為空{OS_SAFETY_CRITICAL_EXCEPTION(); //執(zhí)行安全檢測異常函數(shù)return ((void *)0); //返回空消息,停止執(zhí)行}
#endifif (p_msg_q->NbrEntries == (OS_MSG_QTY)0) //(1)//如果消息隊(duì)列沒有消息{*p_msg_size = (OS_MSG_SIZE)0; //返回消息長度為0if (p_ts != (CPU_TS *)0) //如果 p_ts 非空{*p_ts = (CPU_TS )0; //清零 p_ts}*p_err = OS_ERR_Q_EMPTY; //錯(cuò)誤類型為“隊(duì)列沒消息”return ((void *)0); //返回空消息,停止執(zhí)行}/* 如果消息隊(duì)列有消息 */p_msg = p_msg_q->OutPtr; //(2)//從隊(duì)列的出口端提取消息p_void = p_msg->MsgPtr; //(3)//提取消息內(nèi)容*p_msg_size = p_msg->MsgSize; //(4)//提取消息長度if (p_ts != (CPU_TS *)0) //(5)//如果 p_ts 非空{*p_ts = p_msg->MsgTS; //獲取消息被發(fā)布時(shí)的時(shí)間戳}p_msg_q->OutPtr = p_msg->NextPtr; //(6)//修改隊(duì)列的出隊(duì)指針if (p_msg_q->OutPtr == (OS_MSG *)0) //(7)//如果隊(duì)列沒有消息了{p_msg_q->InPtr = (OS_MSG *)0; //清零出隊(duì)指針p_msg_q->NbrEntries = (OS_MSG_QTY)0; //清零消息數(shù)}else//(8)//如果隊(duì)列還有消息{p_msg_q->NbrEntries--; //隊(duì)列的消息數(shù)減1}/* 從消息隊(duì)列提取完消息信息后,將消息釋放回消息池供繼續(xù)使用 */p_msg->NextPtr = OSMsgPool.NextPtr; //(9)//消息插回消息池OSMsgPool.NextPtr = p_msg;OSMsgPool.NbrFree++; //(10)//消息池的可用消息數(shù)加1OSMsgPool.NbrUsed--; //(11)//消息池的已用消息數(shù)減1*p_err = OS_ERR_NONE; //錯(cuò)誤類型為“無錯(cuò)誤”return (p_void); //(12)//返回消息內(nèi)容
}
- (1):如果消息隊(duì)列目前沒有可用消息,返回消息長度為0, 并且返回錯(cuò)誤類型為“隊(duì)列沒消息”的錯(cuò)誤代碼和空消息,停止執(zhí)行。
- (2):而如果隊(duì)列中有消息,則從隊(duì)列的出口端提取消息。
- (3):提取消息內(nèi)容。
- (4):提取消息長度。
- (5):如果p_ts非空,獲取消息入隊(duì)時(shí)的時(shí)間戳。
- (6):修改隊(duì)列的出隊(duì)指針。
- (7):如果隊(duì)列沒有消息了,就將出隊(duì)指針與消息個(gè)數(shù)清零。
- (8):如果隊(duì)列還有消息,隊(duì)列的消息個(gè)數(shù)減1。
- (9):消息插回消息池,以便重復(fù)利用。
- (10):消息池的可用消息數(shù)加1。
- (11):消息池的已用消息數(shù)減1。
- (12):返回消息內(nèi)容。
3. OS_Pend()函數(shù)
OS_Pend()函數(shù)將當(dāng)前任務(wù)脫離就緒列表, 并根據(jù)用戶指定的阻塞時(shí)間插入節(jié)拍列表和隊(duì)列等待列表, 然后打開調(diào)度器,但不進(jìn)行調(diào)度
void OS_Pend (OS_PEND_DATA *p_pend_data, //待插入等待列表的元素OS_PEND_OBJ *p_obj, //等待的內(nèi)核對象OS_STATE pending_on, //等待哪種對象內(nèi)核OS_TICK timeout) //等待期限
{OS_PEND_LIST *p_pend_list;OSTCBCurPtr->PendOn = pending_on; //資源不可用,開始等待OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK; //正常等待中OS_TaskBlock(OSTCBCurPtr,timeout); //阻塞當(dāng)前運(yùn)行任務(wù),如果 timeout非0,把任務(wù)插入的節(jié)拍列表if (p_obj != (OS_PEND_OBJ *)0) //如果等待對象非空{p_pend_list = &p_obj->PendList; //獲取對象的等待列表到p_pend_listp_pend_data->PendObjPtr = p_obj; //保存要等待的對象OS_PendDataInit((OS_TCB *)OSTCBCurPtr, //初始化 p_pend_data(待插入等待列表)(OS_PEND_DATA *)p_pend_data,(OS_OBJ_QTY )1);//按優(yōu)先級將p_pend_data插入等待列表OS_PendListInsertPrio(p_pend_list,p_pend_data);}else//如果等待對象為空{OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY )0; //清零當(dāng)前任務(wù)的等待域數(shù)據(jù)OSTCBCurPtr->PendDataTblPtr = (OS_PEND_DATA *)0;}
#if OS_CFG_DBG_EN > 0u//如果啟用了調(diào)試代碼和變量OS_PendDbgNameAdd(p_obj, //更新信號量的 DbgNamePtr元素為其等待OSTCBCurPtr);//列表中優(yōu)先級最高的任務(wù)的名稱。
#endif
}