国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

百度網(wǎng)站做防水補(bǔ)漏seo01

百度網(wǎng)站做防水補(bǔ)漏,seo01,宜昌網(wǎng)站建設(shè)制作公司,三合一網(wǎng)站管理系統(tǒng)寫在前面: 由于時(shí)間的不足與學(xué)習(xí)的碎片化,寫博客變得有些奢侈。 但是對于記錄學(xué)習(xí)(忘了以后能快速復(fù)習(xí))的渴望一天天變得強(qiáng)烈。 既然如此 不如以天為單位,以時(shí)間為順序,僅僅將博客當(dāng)做一個(gè)知識學(xué)習(xí)的目錄&a…

寫在前面:
由于時(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
}
http://m.aloenet.com.cn/news/43864.html

相關(guān)文章:

  • 醫(yī)療類網(wǎng)站源碼網(wǎng)絡(luò)推廣網(wǎng)上營銷
  • 網(wǎng)頁創(chuàng)建網(wǎng)站如何免費(fèi)自己創(chuàng)建網(wǎng)站
  • asp.net旅游網(wǎng)站管理系統(tǒng)代碼軟文推廣多少錢一篇
  • 做網(wǎng)站專題需要什么軟件湖南靠譜關(guān)鍵詞優(yōu)化
  • 長治做網(wǎng)站公司網(wǎng)絡(luò)服務(wù)公司
  • 購物網(wǎng)站 服務(wù)器 帶寬 多大360搜索引擎地址
  • 網(wǎng)站開發(fā)怎么使用sharepoint網(wǎng)站推廣優(yōu)化外包便宜
  • 企業(yè)做的網(wǎng)站推廣方案的步驟深圳網(wǎng)站建設(shè)哪家好
  • 公司網(wǎng)站建設(shè)需要什么資質(zhì)購物網(wǎng)站頁面設(shè)計(jì)
  • 怎么選擇一家好的網(wǎng)站建設(shè)公司360優(yōu)化大師
  • 網(wǎng)站制作哪家專業(yè)微商怎么找客源人脈
  • 公司企業(yè)網(wǎng)站免費(fèi)建設(shè)網(wǎng)絡(luò)營銷促銷方案
  • 做極速賽車網(wǎng)站公眾號推廣
  • 在百度網(wǎng)站備案查詢上顯示未備案是什么意思網(wǎng)頁設(shè)計(jì)素材
  • 所有政府網(wǎng)站必須做等保嗎sem運(yùn)營是什么意思
  • 政務(wù)服務(wù) 網(wǎng)站 建設(shè)方案朋友圈推廣平臺
  • 網(wǎng)站收錄低的原因百度云網(wǎng)頁版登錄入口
  • 住房城鄉(xiāng)建設(shè)部辦公廳網(wǎng)站口碑營銷公司
  • 番禺區(qū)網(wǎng)站設(shè)計(jì)線上推廣的方式有哪些
  • 關(guān)于做美食的小視頻網(wǎng)站晚上免費(fèi)b站軟件
  • 石家莊個(gè)人誰做網(wǎng)站廈門百度關(guān)鍵詞推廣
  • 網(wǎng)站優(yōu)化怎樣做網(wǎng)絡(luò)營銷整合推廣
  • 個(gè)人工作室可以做哪些項(xiàng)目win優(yōu)化大師怎么樣
  • 北京網(wǎng)站建設(shè)招聘網(wǎng)站域名查詢系統(tǒng)
  • wordpress 刪除略縮圖關(guān)鍵詞seo優(yōu)化公司
  • 做旅游銷售網(wǎng)站平臺ppt模板網(wǎng)頁設(shè)計(jì)的流程
  • 網(wǎng)站頁面小圖標(biāo)怎么做深圳優(yōu)化公司排名
  • 晾衣架 東莞網(wǎng)站建設(shè)百度一下點(diǎn)擊搜索
  • 關(guān)于優(yōu)化網(wǎng)站建設(shè)的方案怎么可以在百度發(fā)布信息
  • 泰州網(wǎng)站制作案例上海專業(yè)做網(wǎng)站