溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

消息隊列Queue的含義和作用

發(fā)布時間:2021-06-16 09:58:26 來源:億速云 閱讀:408 作者:chen 欄目:web開發(fā)

這篇文章主要介紹“消息隊列Queue的含義和作用”,在日常操作中,相信很多人在消息隊列Queue的含義和作用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”消息隊列Queue的含義和作用”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

隊列(Queue)是一種常用于任務(wù)間通信的數(shù)據(jù)結(jié)構(gòu)。任務(wù)能夠從隊列里面讀取消息,當隊列中的消息為空時,掛起讀取任務(wù);當隊列中有新消息時,掛起的讀取任務(wù)被喚醒并處理新消息。任務(wù)也能夠往隊列里寫入消息,當隊列已經(jīng)寫滿消息時,掛起寫入任務(wù);當隊列中有空閑消息節(jié)點時,掛起的寫入任務(wù)被喚醒并寫入消息。如果將讀隊列和寫隊列的超時時間設(shè)置為0,則不會掛起任務(wù),接口會直接返回,這就是非阻塞模式。消息隊列提供了異步處理機制,允許將一個消息放入隊列,但不立即處理。同時隊列還有緩沖消息的作用。

本文通過分析鴻蒙輕內(nèi)核隊列模塊的源碼,掌握隊列使用上的差異。本文中所涉及的源碼,以O(shè)penHarmony  LiteOS-M內(nèi)核為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。

接下來,我們看下隊列的結(jié)構(gòu)體,隊列初始化,隊列常用操作的源代碼。

1、隊列結(jié)構(gòu)體定義和常用宏定義

1.1 隊列結(jié)構(gòu)體定義

在文件kernel\include\los_queue.h中定義隊列控制塊結(jié)構(gòu)體為LosQueueCB,結(jié)構(gòu)體源代碼如下。隊列狀態(tài).queueState取值OS_QUEUE_UNUSED、OS_QUEUE_INUSED,其他結(jié)構(gòu)體成員見注釋部分。

typedef struct {     UINT8 *queue;      /**< 隊列內(nèi)存空間的指針 */     UINT16 queueState; /**< 隊列的使用狀態(tài) */     UINT16 queueLen;   /**< 隊列長度,即消息數(shù)量 */     UINT16 queueSize;  /**< 消息節(jié)點大小 */     UINT16 queueID;    /**< 隊列編號  */     UINT16 queueHead;  /**< 消息頭節(jié)點位置 */     UINT16 queueTail;  /**< 消息尾節(jié)點位置 */     UINT16 readWriteableCnt[OS_READWRITE_LEN]; /**< 2維數(shù)組,可讀、可寫的消息數(shù)量, 0:可讀, 1:可寫 */     LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /**< 2維雙向鏈表數(shù)組,阻塞讀、寫任務(wù)的雙向鏈表, 0:讀鏈表, 1:寫鏈表 */     LOS_DL_LIST memList; /**< 內(nèi)存節(jié)點雙向鏈表 */ } LosQueueCB;

 1.2 隊列常用宏定義

系統(tǒng)支持創(chuàng)建多少隊列是根據(jù)開發(fā)板情況使用宏LOSCFG_BASE_IPC_QUEUE_LIMIT定義的,每一個隊列queueID是queueID類型的,取值為[0,LOSCFG_BASE_IPC_QUEUE_LIMIT),表示隊列池中各個隊列的編號。

⑴處的宏從隊列池中獲取指定隊列編號QueueID對應(yīng)的隊列控制塊。⑵處根據(jù)雙向鏈表節(jié)點readWriteList[OS_QUEUE_WRITE]獲取隊列控制塊內(nèi)存地址。

⑴    #define GET_QUEUE_HANDLE(QueueID) (((LosQueueCB *)g_allQueue) + (QueueID))  ⑵    #define GET_QUEUE_LIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosQueueCB, readWriteList[OS_QUEUE_WRITE])

另外,隊列中還提供了比較重要的隊列讀取消息操作相關(guān)的枚舉和宏。枚舉QueueReadWrite區(qū)分隊列的讀和寫,枚舉QueueHeadTail區(qū)分隊列的首和尾,枚舉QueuePointOrNot區(qū)分讀寫消息時是使用值還是指針。

隊列的操作類型使用3比特位的數(shù)字來表示,見宏OS_QUEUE_OPERATE_TYPE的定義,其中高1位表示讀寫數(shù)值還是讀寫指針地址,中1位表示隊首還是隊尾,低1位表示讀取還是寫入。枚舉和宏的定義如下:

typedef enum {     OS_QUEUE_READ,     OS_QUEUE_WRITE } QueueReadWrite;  typedef enum {     OS_QUEUE_HEAD,     OS_QUEUE_TAIL } QueueHeadTail;  typedef enum {     OS_QUEUE_NOT_POINT,     OS_QUEUE_POINT } QueuePointOrNot;  #define OS_QUEUE_OPERATE_TYPE(ReadOrWrite, HeadOrTail, PointOrNot)  \                 (((UINT32)(PointOrNot) << 2) | ((UINT32)(HeadOrTail) << 1) | (ReadOrWrite)) #define OS_QUEUE_READ_WRITE_GET(type) ((type) & (0x01)) #define OS_QUEUE_READ_HEAD     (OS_QUEUE_READ | (OS_QUEUE_HEAD << 1)) #define OS_QUEUE_READ_TAIL     (OS_QUEUE_READ | (OS_QUEUE_TAIL << 1)) #define OS_QUEUE_WRITE_HEAD    (OS_QUEUE_WRITE | (OS_QUEUE_HEAD << 1)) #define OS_QUEUE_WRITE_TAIL    (OS_QUEUE_WRITE | (OS_QUEUE_TAIL << 1)) #define OS_QUEUE_OPERATE_GET(type) ((type) & (0x03)) #define OS_QUEUE_IS_POINT(type)    ((type) & (0x04)) #define OS_QUEUE_IS_READ(type)     (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_READ) #define OS_QUEUE_IS_WRITE(type)    (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_WRITE) #define OS_READWRITE_LEN           2

 2、隊列初始化

隊列在內(nèi)核中默認開啟,用戶可以通過宏LOSCFG_BASE_IPC_QUEUE進行關(guān)閉。開啟隊列的情況下,在系統(tǒng)啟動時,在kernel\src\los_init.c中調(diào)用OsQueueInit()進行隊列模塊初始化。下面,我們分析下隊列初始化的代碼。

⑴為隊列申請內(nèi)存,如果申請失敗,則返回錯誤。⑵初始化雙向循環(huán)鏈表g_freeQueueList,維護未使用的隊列。⑶循環(huán)每一個隊列進行初始化,為每一個隊列節(jié)點指定索引queueID,并把隊列節(jié)點插入未使用隊列雙向鏈表g_freeQueueList。代碼上可以看出,掛在未使用隊列雙向鏈表上的節(jié)點是每個隊列控制塊的寫阻塞任務(wù)鏈表節(jié)點.readWriteList[OS_QUEUE_WRITE]。

LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID) {     LosQueueCB *queueNode = NULL;     UINT16 index;      if (LOSCFG_BASE_IPC_QUEUE_LIMIT == 0) {         return LOS_ERRNO_QUEUE_MAXNUM_ZERO;     }  ⑴  g_allQueue = (LosQueueCB *)LOS_MemAlloc(m_aucSysMem0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB));     if (g_allQueue == NULL) {         return LOS_ERRNO_QUEUE_NO_MEMORY;     }      (VOID)memset_s(g_allQueue, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB),                    0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB));  ⑵  LOS_ListInit(&g_freeQueueList); ⑶  for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {         queueNode = ((LosQueueCB *)g_allQueue) + index;         queueNode->queueID = index;         LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);     }      return LOS_OK; }

 3、隊列常用操作

3.1 隊列創(chuàng)建

創(chuàng)建隊列函數(shù)是LOS_QueueCreate(),先看看該函數(shù)的參數(shù):queueName是隊列名稱,實際上并沒有使用。len是隊列中消息的數(shù)量,queueID是隊列編號,flags保留未使用。maxMsgSize是隊列中每條消息的最大大小。

我們分析下創(chuàng)建隊列的代碼。⑴處對參數(shù)進行校驗,隊列編碼不能為空,隊列消息長度不能太大,隊列消息數(shù)量和隊列消息大小不能為0。⑵處計算消息的實際最大大小msgSize,即maxMsgSize  +  sizeof(UINT32)消息最大大小再加4個字節(jié),在消息的最后4個字節(jié)用來保存消息的實際長度。然后調(diào)用⑶處函數(shù)LOS_MemAlloc()為對隊列動態(tài)申請內(nèi)存,如果內(nèi)存申請失敗,則返回錯誤碼。

⑷處判斷g_freeQueueList是否為空,如果沒有可以使用的隊列,釋放前文申請的內(nèi)存。⑸處如果g_freeQueueList不為空,則獲取第一個可用的隊列節(jié)點,接著從雙向鏈表g_freeQueueList中刪除,然后調(diào)用宏GET_QUEUE_LIST獲取LosQueueCB  *queueCB,初始化創(chuàng)建的隊列信息,包含隊列的長度.queueLen、消息大小.queueSize,隊列內(nèi)存空間.queue,消息狀態(tài).queueState,可讀的數(shù)量.readWriteableCnt[OS_QUEUE_READ]為0,可寫的數(shù)量readWriteableCnt[OS_QUEUE_WRITE]為隊列消息長度len,隊列頭位置.queueHead和尾位置.queueTail為0。

⑹初始化雙向鏈表.readWriteList[OS_QUEUE_READ],阻塞在這個隊列上的讀消息任務(wù)會掛在這個鏈表上。初始化雙向鏈表.readWriteList[OS_QUEUE_WRITE],阻塞在這個隊列上的寫消息任務(wù)會掛在這個鏈表上。初始化雙向鏈表.memList。⑺賦值給輸出參數(shù)*queueID,后續(xù)程序使用這個隊列編號對隊列進行其他操作。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName,                                              UINT16 len,                                              UINT32 *queueID,                                              UINT32 flags,                                              UINT16 maxMsgSize) {     LosQueueCB *queueCB = NULL;     UINT32 intSave;     LOS_DL_LIST *unusedQueue = NULL;     UINT8 *queue = NULL;     UINT16 msgSize;      (VOID)queueName;     (VOID)flags;  ⑴  if (queueID == NULL) {         return LOS_ERRNO_QUEUE_CREAT_PTR_NULL;     }      if (maxMsgSize > (OS_NULL_SHORT - sizeof(UINT32))) {         return LOS_ERRNO_QUEUE_SIZE_TOO_BIG;     }      if ((len == 0) || (maxMsgSize == 0)) {         return LOS_ERRNO_QUEUE_PARA_ISZERO;     } ⑵  msgSize = maxMsgSize + sizeof(UINT32);      /* Memory allocation is time-consuming, to shorten the time of disable interrupt,        move the memory allocation to here. */ ⑶  queue = (UINT8 *)LOS_MemAlloc(m_aucSysMem0, len * msgSize);     if (queue == NULL) {         return LOS_ERRNO_QUEUE_CREATE_NO_MEMORY;     }      intSave = LOS_IntLock(); ⑷  if (LOS_ListEmpty(&g_freeQueueList)) {         LOS_IntRestore(intSave);         (VOID)LOS_MemFree(m_aucSysMem0, queue);         return LOS_ERRNO_QUEUE_CB_UNAVAILABLE;     }  ⑸  unusedQueue = LOS_DL_LIST_FIRST(&(g_freeQueueList));     LOS_ListDelete(unusedQueue);     queueCB = (GET_QUEUE_LIST(unusedQueue));     queueCB->queueLen = len;     queueCB->queueSize = msgSize;     queueCB->queue = queue;     queueCB->queueState = OS_QUEUE_INUSED;     queueCB->readWriteableCnt[OS_QUEUE_READ] = 0;     queueCB->readWriteableCnt[OS_QUEUE_WRITE] = len;     queueCB->queueHead = 0;     queueCB->queueTail = 0; ⑹  LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]);     LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]);     LOS_ListInit(&queueCB->memList);     LOS_IntRestore(intSave);  ⑺  *queueID = queueCB->queueID;      OsHookCall(LOS_HOOK_TYPE_QUEUE_CREATE, queueCB);      return LOS_OK; }

 3.2 隊列刪除

我們可以使用函數(shù)LOS_QueueDelete(UINT32 queueID)來刪除隊列,下面通過分析源碼看看如何刪除隊列的。

⑴處判斷隊列queueID是否超過LOSCFG_BASE_IPC_QUEUE_LIMIT,如果超過則返回錯誤碼。如果隊列編號沒有問題,獲取隊列控制塊LosQueueCB  *queueCB。⑵處判斷要刪除的隊列處于未使用狀態(tài),則跳轉(zhuǎn)到錯誤標簽QUEUE_END進行處理。⑶如果隊列的阻塞讀、阻塞寫任務(wù)列表不為空,或內(nèi)存節(jié)點鏈表不為空,則不允許刪除,跳轉(zhuǎn)到錯誤標簽進行處理。⑷處檢驗隊列的可讀、可寫數(shù)量是否出錯。

⑸處使用指針UINT8  *queue保存隊列的內(nèi)存空間,⑹處把.queue置空,把.queueState設(shè)置為未使用OS_QUEUE_UNUSED,并把隊列節(jié)點插入未使用隊列雙向鏈表g_freeQueueList。接下來會需要調(diào)用⑺處函數(shù)LOS_MemFree()釋放隊列內(nèi)存空間。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID) {     LosQueueCB *queueCB = NULL;     UINT8 *queue = NULL;     UINT32 intSave;     UINT32 ret;  ⑴  if (queueID >= LOSCFG_BASE_IPC_QUEUE_LIMIT) {         return LOS_ERRNO_QUEUE_NOT_FOUND;     }      intSave = LOS_IntLock();     queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID); ⑵  if (queueCB->queueState == OS_QUEUE_UNUSED) {         ret = LOS_ERRNO_QUEUE_NOT_CREATE;         goto QUEUE_END;     }  ⑶  if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_READ])) {         ret = LOS_ERRNO_QUEUE_IN_TSKUSE;         goto QUEUE_END;     }      if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_WRITE])) {         ret = LOS_ERRNO_QUEUE_IN_TSKUSE;         goto QUEUE_END;     }      if (!LOS_ListEmpty(&queueCB->memList)) {         ret = LOS_ERRNO_QUEUE_IN_TSKUSE;         goto QUEUE_END;     }  ⑷  if ((queueCB->readWriteableCnt[OS_QUEUE_WRITE] + queueCB->readWriteableCnt[OS_QUEUE_READ]) !=         queueCB->queueLen) {         ret = LOS_ERRNO_QUEUE_IN_TSKWRITE;         goto QUEUE_END;     }  ⑸  queue = queueCB->queue; ⑹  queueCB->queue = (UINT8 *)NULL;     queueCB->queueState = OS_QUEUE_UNUSED;     LOS_ListAdd(&g_freeQueueList, &queueCB->readWriteList[OS_QUEUE_WRITE]);     LOS_IntRestore(intSave);      OsHookCall(LOS_HOOK_TYPE_QUEUE_DELETE, queueCB);  ⑺  ret = LOS_MemFree(m_aucSysMem0, (VOID *)queue);     return ret;  QUEUE_END:     LOS_IntRestore(intSave);     return ret; }

下面就來看看隊列的讀寫,有2點需要注意:

  • 隊首、隊尾的讀寫

只支持隊首讀取,不能隊尾讀取,否則就不算隊列了。除了正常的隊尾寫消息外,還提供插隊機制,支持從隊首寫入。

  • 隊列消息數(shù)據(jù)內(nèi)容

往隊列中寫入的消息的類型有2種,即支持按地址寫入和按值寫入(帶拷貝)。按哪種類型寫入,就需要配對的按相應(yīng)的類型去讀取。

隊列讀取接口的類別,歸納如下:

消息隊列Queue的含義和作用

3.3 隊列讀取

我們知道有2個隊列讀取方法,按指針地址讀取的函數(shù)LOS_QueueRead()和按消息數(shù)值讀取的函數(shù)LOS_QueueReadCopy()。我們先看下函數(shù)LOS_QueueRead(),該函數(shù)的參數(shù)有4個,隊列編號queueID,存放讀取到的消息的緩沖區(qū)地址*bufferAddr,存放讀取到的消息的緩沖區(qū)大小bufferSize,讀隊列消息的等待超時時間timeOut。代碼如下,我們分析下代碼。

⑴處校驗傳入?yún)?shù),隊列編號不能超出限制,傳入的指針不能為空,緩沖大小不能為0。如果timeout不為零,不能在中斷中讀取隊列。⑵處操作類型表示隊首讀取消息指針,然后調(diào)用函數(shù)OsQueueOperate()進一步操作隊列。

LITE_OS_SEC_TEXT UINT32 LOS_QueueRead(UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeOut) {     UINT32 ret;     UINT32 operateType;  ⑴  ret = OsQueueReadParameterCheck(queueID, bufferAddr, &bufferSize, timeOut);     if (ret != LOS_OK) {         return ret;     }  ⑵  operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_READ, OS_QUEUE_HEAD, OS_QUEUE_POINT);      OsHookCall(LOS_HOOK_TYPE_QUEUE_READ, (LosQueueCB *)GET_QUEUE_HANDLE(queueID));      return OsQueueOperate(queueID, operateType, bufferAddr, &bufferSize, timeOut); }

我們進一步分析下函數(shù)OsQueueOperate(),這是是比較通用的封裝,讀取,寫入都會調(diào)用這個函數(shù),我們以讀取隊列為例分析這個函數(shù)。⑴處獲取隊列的操作類型,為讀取操作。⑵處先調(diào)用函數(shù)OsQueueOperateParamCheck()進行參數(shù)校驗,校驗隊列是使用中的隊列,并對讀寫消息大小進行校驗。⑶處如果可讀數(shù)量為0,無法讀取時,如果是零等待則返回錯誤碼。如果當前鎖任務(wù)調(diào)度,跳出函數(shù)執(zhí)行。否則,執(zhí)行⑷把當前任務(wù)放入隊列的讀取消息阻塞隊列,然后觸發(fā)任務(wù)調(diào)度,后續(xù)的代碼暫時不再執(zhí)行。如果可讀的數(shù)量不為0,可以繼續(xù)讀取時,執(zhí)行⑹處代碼把可讀數(shù)量減1,然后繼續(xù)執(zhí)行⑺處代碼讀取隊列。

等讀取隊列阻塞超時,或者隊列可以讀取后,繼續(xù)執(zhí)行⑸處的代碼。如果是發(fā)生超時,隊列還不能讀取,更改任務(wù)狀態(tài),跳出函數(shù)執(zhí)行。如果隊列可以讀取了,繼續(xù)執(zhí)行⑺處代碼讀取隊列。⑻處在成功讀取隊列后,如果有任務(wù)阻塞在寫入隊列,則獲取阻塞鏈表中的第一個任務(wù)resumedTask,然后調(diào)用喚醒函數(shù)OsSchedTaskWake()把待恢復(fù)的任務(wù)放入就緒隊列,觸發(fā)一次任務(wù)調(diào)度。如果無阻塞任務(wù),則把可寫入的數(shù)量加1。

UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeOut) {     LosQueueCB *queueCB = NULL;     LosTaskCB *resumedTask = NULL;     UINT32 ret; ⑴  UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);     UINT32 readWriteTmp = !readWrite;      UINT32 intSave = LOS_IntLock();      queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID); ⑵  ret = OsQueueOperateParamCheck(queueCB, operateType, bufferSize);     if (ret != LOS_OK) {         goto QUEUE_END;     }  ⑶  if (queueCB->readWriteableCnt[readWrite] == 0) {         if (timeOut == LOS_NO_WAIT) {             ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL;             goto QUEUE_END;         }          if (g_losTaskLock) {             ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK;             goto QUEUE_END;         }          LosTaskCB *runTsk = (LosTaskCB *)g_losTask.runTask; ⑷      OsSchedTaskWait(&queueCB->readWriteList[readWrite], timeOut);         LOS_IntRestore(intSave);         LOS_Schedule();          intSave = LOS_IntLock(); ⑸      if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) {             runTsk->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;             ret = LOS_ERRNO_QUEUE_TIMEOUT;             goto QUEUE_END;         }     } else { ⑹       queueCB->readWriteableCnt[readWrite]--;     }  ⑺   OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize);   ⑻  if (!LOS_ListEmpty(&queueCB->readWriteList[readWriteTmp])) {         resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[readWriteTmp]));         OsSchedTaskWake(resumedTask);         LOS_IntRestore(intSave);         LOS_Schedule();         return LOS_OK;     } else { ⑼      queueCB->readWriteableCnt[readWriteTmp]++;     }  QUEUE_END:     LOS_IntRestore(intSave);     return ret; }

我們再繼續(xù)看下函數(shù)OsQueueBufferOperate()是具體如何讀取隊列的。⑴處switch-case語句根據(jù)操作類型獲取操作位置。對于⑵頭部讀取的情況,先獲取讀取位置queuePosition。然后,如果當前頭節(jié)點位置.queueHead加1等于隊列消息長度,頭節(jié)點位置.queueHead設(shè)置為0,否則加1。對于⑶頭部寫入的情況,如果當前頭節(jié)點位置.queueHead等于0,頭節(jié)點位置.queueHead設(shè)置為隊列消息長度減1即queueCB->queueLen  -  1,否則頭節(jié)點位置.queueHead減1即可。然后,獲取要寫入的位置queuePosition。對于⑷尾部寫入的情況,先獲取寫入位置queuePosition。然后,如果當前尾節(jié)點位置.queueTail加1等于隊列消息長度,尾節(jié)點位置.queueTail設(shè)置為0,否則加1。

⑸處基于獲取的隊列讀取位置獲取隊列消息節(jié)點queueNode。⑹處判斷操作類型如果是按指針讀寫消息,直接讀取消息節(jié)點的數(shù)據(jù)寫入指針對應(yīng)的緩沖區(qū)*(UINT32  *)bufferAddr,或直接把指針對應(yīng)的緩沖區(qū)*(UINT32  *)bufferAddr數(shù)據(jù)寫入消息節(jié)點即可。我們接著看如何按數(shù)數(shù)據(jù)讀寫消息,⑺處代碼用于讀取數(shù)據(jù)消息。每個消息節(jié)點的后4個字節(jié)保存的是消息的長度,首先獲取消息的長度msgDataSize,然后把消息內(nèi)容讀取到bufferAddr。再看看⑻處如何寫入隊列消息,首先把消息內(nèi)容寫入到queueNode,然后再把消息長度的內(nèi)容寫入到queueNode  + queueCB->queueSize - sizeof(UINT32),就是每個消息節(jié)點的后4字節(jié)。

static INLINE VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType,                                                                 VOID *bufferAddr, UINT32 *bufferSize) {     UINT8 *queueNode = NULL;     UINT32 msgDataSize;     UINT16 queuePosion;     errno_t rc;      /* get the queue position */ ⑴  switch (OS_QUEUE_OPERATE_GET(operateType)) {         case OS_QUEUE_READ_HEAD: ⑵          queuePosion = queueCB->queueHead;             ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++);             break;          case OS_QUEUE_WRITE_HEAD: ⑶          (queueCB->queueHead == 0) ? (queueCB->queueHead = (queueCB->queueLen - 1)) : (--queueCB->queueHead);             queuePosion = queueCB->queueHead;             break;          case OS_QUEUE_WRITE_TAIL: ⑷          queuePosion = queueCB->queueTail;             ((queueCB->queueTail + 1) == queueCB->queueLen) ? (queueCB->queueTail = 0) : (queueCB->queueTail++);             break;          default:             PRINT_ERR("invalid queue operate type!\n");             return;     }  ⑸  queueNode = &(queueCB->queue[(queuePosion * (queueCB->queueSize))]);  ⑹  if (OS_QUEUE_IS_POINT(operateType)) {       if (OS_QUEUE_IS_READ(operateType)) {             *(UINT32 *)bufferAddr = *(UINT32 *)(VOID *)queueNode;         } else {             *(UINT32 *)(VOID *)queueNode = *(UINT32 *)bufferAddr;  // change to pp when calling OsQueueOperate         }     } else { ⑺      if (OS_QUEUE_IS_READ(operateType)) {             msgDataSize = *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32)));             rc = memcpy_s((VOID *)bufferAddr, *bufferSize, (VOID *)queueNode, msgDataSize);             if (rc != EOK) {                 PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc);                 return;             }              *bufferSize = msgDataSize;         } else { ⑻          *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32))) = *bufferSize;             rc = memcpy_s((VOID *)queueNode, queueCB->queueSize, (VOID *)bufferAddr, *bufferSize);             if (rc != EOK) {                 PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc);                 return;             }         }     } }

 3.4 隊列寫入

我們知道,有4個隊列寫入方法,2個隊尾寫入,2個隊首寫入,分別包含按指針地址寫入消息和按數(shù)值寫入消息。LOS_QueueWrite()會調(diào)用LOS_QueueWriteCopy(),LOS_QueueWriteHead()會調(diào)用LOS_QueueWriteHeadCopy(),然后指定不同的操作類型后,會進一步調(diào)用前文已經(jīng)分析過的函數(shù)OsQueueOperate()。

到此,關(guān)于“消息隊列Queue的含義和作用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI