鴻蒙輕內核M核源碼分析系列十三 消息隊列Queue
隊列(Queue)是一種常用于任務間通信的數據結構。任務能夠從隊列里面讀取消息,當隊列中的消息為空時,掛起讀取任務;當隊列中有新消息時,掛起的讀取任務被喚醒并處理新消息。任務也能夠往隊列里寫入消息,當隊列已經寫滿消息時,掛起寫入任務;當隊列中有空閑消息節點時,掛起的寫入任務被喚醒并寫入消息。如果將讀隊列和寫隊列的超時時間設置為0,則不會掛起任務,接口會直接返回,這就是非阻塞模式。消息隊列提供了異步處理機制,允許將一個消息放入隊列,但不立即處理。同時隊列還有緩沖消息的作用。
本文通過分析鴻蒙輕內核隊列模塊的源碼,掌握隊列使用上的差異。
接下來,我們看下隊列的結構體,隊列初始化,隊列常用操作的源代碼。
1、隊列結構體定義和常用宏定義
1.1 隊列結構體定義
在文件kernel\include\los_queue.h中定義隊列控制塊結構體為LosQueueCB,結構體源代碼如下。隊列狀態.queueState取值OS_QUEUE_UNUSED、OS_QUEUE_INUSED,其他結構體成員見注釋部分。
- typedef struct {
- UINT8 *queue; /**< 隊列內存空間的指針 */
- UINT16 queueState; /**< 隊列的使用狀態 */
- UINT16 queueLen; /**< 隊列長度,即消息數量 */
- UINT16 queueSize; /**< 消息節點大小 */
- UINT16 queueID; /**< 隊列編號 */
- UINT16 queueHead; /**< 消息頭節點位置 */
- UINT16 queueTail; /**< 消息尾節點位置 */
- UINT16 readWriteableCnt[OS_READWRITE_LEN]; /**< 2維數組,可讀、可寫的消息數量, 0:可讀, 1:可寫 */
- LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /**< 2維雙向鏈表數組,阻塞讀、寫任務的雙向鏈表, 0:讀鏈表, 1:寫鏈表 */
- LOS_DL_LIST memList; /**< 內存節點雙向鏈表 */
- } LosQueueCB;
1.2 隊列常用宏定義
系統支持創建多少隊列是根據開發板情況使用宏LOSCFG_BASE_IPC_QUEUE_LIMIT定義的,每一個隊列queueID是queueID類型的,取值為[0,LOSCFG_BASE_IPC_QUEUE_LIMIT),表示隊列池中各個隊列的編號。
⑴處的宏從隊列池中獲取指定隊列編號QueueID對應的隊列控制塊。⑵處根據雙向鏈表節點readWriteList[OS_QUEUE_WRITE]獲取隊列控制塊內存地址。
- ⑴ #define GET_QUEUE_HANDLE(QueueID) (((LosQueueCB *)g_allQueue) + (QueueID))
- ⑵ #define GET_QUEUE_LIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosQueueCB, readWriteList[OS_QUEUE_WRITE])
另外,隊列中還提供了比較重要的隊列讀取消息操作相關的枚舉和宏。枚舉QueueReadWrite區分隊列的讀和寫,枚舉QueueHeadTail區分隊列的首和尾,枚舉QueuePointOrNot區分讀寫消息時是使用值還是指針。
隊列的操作類型使用3比特位的數字來表示,見宏OS_QUEUE_OPERATE_TYPE的定義,其中高1位表示讀寫數值還是讀寫指針地址,中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、隊列初始化
隊列在內核中默認開啟,用戶可以通過宏LOSCFG_BASE_IPC_QUEUE進行關閉。開啟隊列的情況下,在系統啟動時,在kernel\src\los_init.c中調用OsQueueInit()進行隊列模塊初始化。下面,我們分析下隊列初始化的代碼。
⑴為隊列申請內存,如果申請失敗,則返回錯誤。⑵初始化雙向循環鏈表g_freeQueueList,維護未使用的隊列。⑶循環每一個隊列進行初始化,為每一個隊列節點指定索引queueID,并把隊列節點插入未使用隊列雙向鏈表g_freeQueueList。代碼上可以看出,掛在未使用隊列雙向鏈表上的節點是每個隊列控制塊的寫阻塞任務鏈表節點.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 隊列創建
創建隊列函數是LOS_QueueCreate(),先看看該函數的參數:queueName是隊列名稱,實際上并沒有使用。len是隊列中消息的數量,queueID是隊列編號,flags保留未使用。maxMsgSize是隊列中每條消息的最大大小。
我們分析下創建隊列的代碼。⑴處對參數進行校驗,隊列編碼不能為空,隊列消息長度不能太大,隊列消息數量和隊列消息大小不能為0。⑵處計算消息的實際最大大小msgSize,即maxMsgSize + sizeof(UINT32)消息最大大小再加4個字節,在消息的最后4個字節用來保存消息的實際長度。然后調用⑶處函數LOS_MemAlloc()為對隊列動態申請內存,如果內存申請失敗,則返回錯誤碼。
⑷處判斷g_freeQueueList是否為空,如果沒有可以使用的隊列,釋放前文申請的內存。⑸處如果g_freeQueueList不為空,則獲取第一個可用的隊列節點,接著從雙向鏈表g_freeQueueList中刪除,然后調用宏GET_QUEUE_LIST獲取LosQueueCB *queueCB,初始化創建的隊列信息,包含隊列的長度.queueLen、消息大小.queueSize,隊列內存空間.queue,消息狀態.queueState,可讀的數量.readWriteableCnt[OS_QUEUE_READ]為0,可寫的數量readWriteableCnt[OS_QUEUE_WRITE]為隊列消息長度len,隊列頭位置.queueHead和尾位置.queueTail為0。
⑹初始化雙向鏈表.readWriteList[OS_QUEUE_READ],阻塞在這個隊列上的讀消息任務會掛在這個鏈表上。初始化雙向鏈表.readWriteList[OS_QUEUE_WRITE],阻塞在這個隊列上的寫消息任務會掛在這個鏈表上。初始化雙向鏈表.memList。⑺賦值給輸出參數*queueID,后續程序使用這個隊列編號對隊列進行其他操作。
- 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 隊列刪除
我們可以使用函數LOS_QueueDelete(UINT32 queueID)來刪除隊列,下面通過分析源碼看看如何刪除隊列的。
⑴處判斷隊列queueID是否超過LOSCFG_BASE_IPC_QUEUE_LIMIT,如果超過則返回錯誤碼。如果隊列編號沒有問題,獲取隊列控制塊LosQueueCB *queueCB。⑵處判斷要刪除的隊列處于未使用狀態,則跳轉到錯誤標簽QUEUE_END進行處理。⑶如果隊列的阻塞讀、阻塞寫任務列表不為空,或內存節點鏈表不為空,則不允許刪除,跳轉到錯誤標簽進行處理。⑷處檢驗隊列的可讀、可寫數量是否出錯。
⑸處使用指針UINT8 *queue保存隊列的內存空間,⑹處把.queue置空,把.queueState設置為未使用OS_QUEUE_UNUSED,并把隊列節點插入未使用隊列雙向鏈表g_freeQueueList。接下來會需要調用⑺處函數LOS_MemFree()釋放隊列內存空間。
- 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點需要注意:
- 隊首、隊尾的讀寫
只支持隊首讀取,不能隊尾讀取,否則就不算隊列了。除了正常的隊尾寫消息外,還提供插隊機制,支持從隊首寫入。
- 隊列消息數據內容
往隊列中寫入的消息的類型有2種,即支持按地址寫入和按值寫入(帶拷貝)。按哪種類型寫入,就需要配對的按相應的類型去讀取。
隊列讀取接口的類別,歸納如下:
3.3 隊列讀取
我們知道有2個隊列讀取方法,按指針地址讀取的函數LOS_QueueRead()和按消息數值讀取的函數LOS_QueueReadCopy()。我們先看下函數LOS_QueueRead(),該函數的參數有4個,隊列編號queueID,存放讀取到的消息的緩沖區地址*bufferAddr,存放讀取到的消息的緩沖區大小bufferSize,讀隊列消息的等待超時時間timeOut。代碼如下,我們分析下代碼。
⑴處校驗傳入參數,隊列編號不能超出限制,傳入的指針不能為空,緩沖大小不能為0。如果timeout不為零,不能在中斷中讀取隊列。⑵處操作類型表示隊首讀取消息指針,然后調用函數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);
- }
我們進一步分析下函數OsQueueOperate(),這是是比較通用的封裝,讀取,寫入都會調用這個函數,我們以讀取隊列為例分析這個函數。⑴處獲取隊列的操作類型,為讀取操作。⑵處先調用函數OsQueueOperateParamCheck()進行參數校驗,校驗隊列是使用中的隊列,并對讀寫消息大小進行校驗。⑶處如果可讀數量為0,無法讀取時,如果是零等待則返回錯誤碼。如果當前鎖任務調度,跳出函數執行。否則,執行⑷把當前任務放入隊列的讀取消息阻塞隊列,然后觸發任務調度,后續的代碼暫時不再執行。如果可讀的數量不為0,可以繼續讀取時,執行⑹處代碼把可讀數量減1,然后繼續執行⑺處代碼讀取隊列。
等讀取隊列阻塞超時,或者隊列可以讀取后,繼續執行⑸處的代碼。如果是發生超時,隊列還不能讀取,更改任務狀態,跳出函數執行。如果隊列可以讀取了,繼續執行⑺處代碼讀取隊列。⑻處在成功讀取隊列后,如果有任務阻塞在寫入隊列,則獲取阻塞鏈表中的第一個任務resumedTask,然后調用喚醒函數OsSchedTaskWake()把待恢復的任務放入就緒隊列,觸發一次任務調度。如果無阻塞任務,則把可寫入的數量加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;
- }
我們再繼續看下函數OsQueueBufferOperate()是具體如何讀取隊列的。⑴處switch-case語句根據操作類型獲取操作位置。對于⑵頭部讀取的情況,先獲取讀取位置queuePosition。然后,如果當前頭節點位置.queueHead加1等于隊列消息長度,頭節點位置.queueHead設置為0,否則加1。對于⑶頭部寫入的情況,如果當前頭節點位置.queueHead等于0,頭節點位置.queueHead設置為隊列消息長度減1即queueCB->queueLen - 1,否則頭節點位置.queueHead減1即可。然后,獲取要寫入的位置queuePosition。對于⑷尾部寫入的情況,先獲取寫入位置queuePosition。然后,如果當前尾節點位置.queueTail加1等于隊列消息長度,尾節點位置.queueTail設置為0,否則加1。
⑸處基于獲取的隊列讀取位置獲取隊列消息節點queueNode。⑹處判斷操作類型如果是按指針讀寫消息,直接讀取消息節點的數據寫入指針對應的緩沖區*(UINT32 *)bufferAddr,或直接把指針對應的緩沖區*(UINT32 *)bufferAddr數據寫入消息節點即可。我們接著看如何按數數據讀寫消息,⑺處代碼用于讀取數據消息。每個消息節點的后4個字節保存的是消息的長度,首先獲取消息的長度msgDataSize,然后把消息內容讀取到bufferAddr。再看看⑻處如何寫入隊列消息,首先把消息內容寫入到queueNode,然后再把消息長度的內容寫入到queueNode + queueCB->queueSize - sizeof(UINT32),就是每個消息節點的后4字節。
- 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個隊首寫入,分別包含按指針地址寫入消息和按數值寫入消息。LOS_QueueWrite()會調用LOS_QueueWriteCopy(),LOS_QueueWriteHead()會調用LOS_QueueWriteHeadCopy(),然后指定不同的操作類型后,會進一步調用前文已經分析過的函數OsQueueOperate()。
小結
本文帶領大家一起剖析了鴻蒙輕內核的隊列模塊的源代碼,包含隊列的結構體、隊列池初始化、隊列創建刪除、讀寫消息等。