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