自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

鴻蒙輕內(nèi)核M核源碼分析系列十三 消息隊(duì)列Queue

開發(fā) 前端
本文帶領(lǐng)大家一起剖析了鴻蒙輕內(nèi)核的隊(duì)列模塊的源代碼,包含隊(duì)列的結(jié)構(gòu)體、隊(duì)列池初始化、隊(duì)列創(chuàng)建刪除、讀寫消息等。

[[403723]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

隊(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)體成員見注釋部分。

  1. typedef struct { 
  2.     UINT8 *queue;      /**< 隊(duì)列內(nèi)存空間的指針 */ 
  3.     UINT16 queueState; /**< 隊(duì)列的使用狀態(tài) */ 
  4.     UINT16 queueLen;   /**< 隊(duì)列長(zhǎng)度,即消息數(shù)量 */ 
  5.     UINT16 queueSize;  /**< 消息節(jié)點(diǎn)大小 */ 
  6.     UINT16 queueID;    /**< 隊(duì)列編號(hào)  */ 
  7.     UINT16 queueHead;  /**< 消息頭節(jié)點(diǎn)位置 */ 
  8.     UINT16 queueTail;  /**< 消息尾節(jié)點(diǎn)位置 */ 
  9.     UINT16 readWriteableCnt[OS_READWRITE_LEN]; /**< 2維數(shù)組,可讀、可寫的消息數(shù)量, 0:可讀, 1:可寫 */ 
  10.     LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /**< 2維雙向鏈表數(shù)組,阻塞讀、寫任務(wù)的雙向鏈表, 0:讀鏈表, 1:寫鏈表 */ 
  11.     LOS_DL_LIST memList; /**< 內(nèi)存節(jié)點(diǎn)雙向鏈表 */ 
  12. } 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)存地址。

  1. ⑴    #define GET_QUEUE_HANDLE(QueueID) (((LosQueueCB *)g_allQueue) + (QueueID)) 
  2.  
  3. ⑵    #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位表示讀取還是寫入。枚舉和宏的定義如下:

  1. typedef enum { 
  2.     OS_QUEUE_READ, 
  3.     OS_QUEUE_WRITE 
  4. } QueueReadWrite; 
  5.  
  6. typedef enum { 
  7.     OS_QUEUE_HEAD, 
  8.     OS_QUEUE_TAIL 
  9. } QueueHeadTail; 
  10.  
  11. typedef enum { 
  12.     OS_QUEUE_NOT_POINT, 
  13.     OS_QUEUE_POINT 
  14. } QueuePointOrNot; 
  15.  
  16. #define OS_QUEUE_OPERATE_TYPE(ReadOrWrite, HeadOrTail, PointOrNot)  \ 
  17.                 (((UINT32)(PointOrNot) << 2) | ((UINT32)(HeadOrTail) << 1) | (ReadOrWrite)) 
  18. #define OS_QUEUE_READ_WRITE_GET(type) ((type) & (0x01)) 
  19. #define OS_QUEUE_READ_HEAD     (OS_QUEUE_READ | (OS_QUEUE_HEAD << 1)) 
  20. #define OS_QUEUE_READ_TAIL     (OS_QUEUE_READ | (OS_QUEUE_TAIL << 1)) 
  21. #define OS_QUEUE_WRITE_HEAD    (OS_QUEUE_WRITE | (OS_QUEUE_HEAD << 1)) 
  22. #define OS_QUEUE_WRITE_TAIL    (OS_QUEUE_WRITE | (OS_QUEUE_TAIL << 1)) 
  23. #define OS_QUEUE_OPERATE_GET(type) ((type) & (0x03)) 
  24. #define OS_QUEUE_IS_POINT(type)    ((type) & (0x04)) 
  25. #define OS_QUEUE_IS_READ(type)     (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_READ) 
  26. #define OS_QUEUE_IS_WRITE(type)    (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_WRITE) 
  27. #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]。

  1. LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID) 
  2.     LosQueueCB *queueNode = NULL
  3.     UINT16 index
  4.  
  5.     if (LOSCFG_BASE_IPC_QUEUE_LIMIT == 0) { 
  6.         return LOS_ERRNO_QUEUE_MAXNUM_ZERO; 
  7.     } 
  8.  
  9. ⑴  g_allQueue = (LosQueueCB *)LOS_MemAlloc(m_aucSysMem0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB)); 
  10.     if (g_allQueue == NULL) { 
  11.         return LOS_ERRNO_QUEUE_NO_MEMORY; 
  12.     } 
  13.  
  14.     (VOID)memset_s(g_allQueue, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB), 
  15.                    0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB)); 
  16.  
  17. ⑵  LOS_ListInit(&g_freeQueueList); 
  18. ⑶  for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) { 
  19.         queueNode = ((LosQueueCB *)g_allQueue) + index
  20.         queueNode->queueID = index
  21.         LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]); 
  22.     } 
  23.  
  24.     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)行其他操作。

  1. LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName, 
  2.                                              UINT16 len, 
  3.                                              UINT32 *queueID, 
  4.                                              UINT32 flags, 
  5.                                              UINT16 maxMsgSize) 
  6.     LosQueueCB *queueCB = NULL
  7.     UINT32 intSave; 
  8.     LOS_DL_LIST *unusedQueue = NULL
  9.     UINT8 *queue = NULL
  10.     UINT16 msgSize; 
  11.  
  12.     (VOID)queueName; 
  13.     (VOID)flags; 
  14.  
  15. ⑴  if (queueID == NULL) { 
  16.         return LOS_ERRNO_QUEUE_CREAT_PTR_NULL; 
  17.     } 
  18.  
  19.     if (maxMsgSize > (OS_NULL_SHORT - sizeof(UINT32))) { 
  20.         return LOS_ERRNO_QUEUE_SIZE_TOO_BIG; 
  21.     } 
  22.  
  23.     if ((len == 0) || (maxMsgSize == 0)) { 
  24.         return LOS_ERRNO_QUEUE_PARA_ISZERO; 
  25.     } 
  26. ⑵  msgSize = maxMsgSize + sizeof(UINT32); 
  27.  
  28.     /* Memory allocation is time-consuming, to shorten the time of disable interrupt, 
  29.        move the memory allocation to here. */ 
  30. ⑶  queue = (UINT8 *)LOS_MemAlloc(m_aucSysMem0, len * msgSize); 
  31.     if (queue == NULL) { 
  32.         return LOS_ERRNO_QUEUE_CREATE_NO_MEMORY; 
  33.     } 
  34.  
  35.     intSave = LOS_IntLock(); 
  36. ⑷  if (LOS_ListEmpty(&g_freeQueueList)) { 
  37.         LOS_IntRestore(intSave); 
  38.         (VOID)LOS_MemFree(m_aucSysMem0, queue); 
  39.         return LOS_ERRNO_QUEUE_CB_UNAVAILABLE; 
  40.     } 
  41.  
  42. ⑸  unusedQueue = LOS_DL_LIST_FIRST(&(g_freeQueueList)); 
  43.     LOS_ListDelete(unusedQueue); 
  44.     queueCB = (GET_QUEUE_LIST(unusedQueue)); 
  45.     queueCB->queueLen = len; 
  46.     queueCB->queueSize = msgSize; 
  47.     queueCB->queue = queue; 
  48.     queueCB->queueState = OS_QUEUE_INUSED; 
  49.     queueCB->readWriteableCnt[OS_QUEUE_READ] = 0; 
  50.     queueCB->readWriteableCnt[OS_QUEUE_WRITE] = len; 
  51.     queueCB->queueHead = 0; 
  52.     queueCB->queueTail = 0; 
  53. ⑹  LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]); 
  54.     LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]); 
  55.     LOS_ListInit(&queueCB->memList); 
  56.     LOS_IntRestore(intSave); 
  57.  
  58. ⑺  *queueID = queueCB->queueID; 
  59.  
  60.     OsHookCall(LOS_HOOK_TYPE_QUEUE_CREATE, queueCB); 
  61.  
  62.     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)存空間。

  1. LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID) 
  2.     LosQueueCB *queueCB = NULL
  3.     UINT8 *queue = NULL
  4.     UINT32 intSave; 
  5.     UINT32 ret; 
  6.  
  7. ⑴  if (queueID >= LOSCFG_BASE_IPC_QUEUE_LIMIT) { 
  8.         return LOS_ERRNO_QUEUE_NOT_FOUND; 
  9.     } 
  10.  
  11.     intSave = LOS_IntLock(); 
  12.     queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID); 
  13. ⑵  if (queueCB->queueState == OS_QUEUE_UNUSED) { 
  14.         ret = LOS_ERRNO_QUEUE_NOT_CREATE; 
  15.         goto QUEUE_END; 
  16.     } 
  17.  
  18. ⑶  if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_READ])) { 
  19.         ret = LOS_ERRNO_QUEUE_IN_TSKUSE; 
  20.         goto QUEUE_END; 
  21.     } 
  22.  
  23.     if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_WRITE])) { 
  24.         ret = LOS_ERRNO_QUEUE_IN_TSKUSE; 
  25.         goto QUEUE_END; 
  26.     } 
  27.  
  28.     if (!LOS_ListEmpty(&queueCB->memList)) { 
  29.         ret = LOS_ERRNO_QUEUE_IN_TSKUSE; 
  30.         goto QUEUE_END; 
  31.     } 
  32.  
  33. ⑷  if ((queueCB->readWriteableCnt[OS_QUEUE_WRITE] + queueCB->readWriteableCnt[OS_QUEUE_READ]) != 
  34.         queueCB->queueLen) { 
  35.         ret = LOS_ERRNO_QUEUE_IN_TSKWRITE; 
  36.         goto QUEUE_END; 
  37.     } 
  38.  
  39. ⑸  queue = queueCB->queue; 
  40. ⑹  queueCB->queue = (UINT8 *)NULL
  41.     queueCB->queueState = OS_QUEUE_UNUSED; 
  42.     LOS_ListAdd(&g_freeQueueList, &queueCB->readWriteList[OS_QUEUE_WRITE]); 
  43.     LOS_IntRestore(intSave); 
  44.  
  45.     OsHookCall(LOS_HOOK_TYPE_QUEUE_DELETE, queueCB); 
  46.  
  47. ⑺  ret = LOS_MemFree(m_aucSysMem0, (VOID *)queue); 
  48.     return ret; 
  49.  
  50. QUEUE_END: 
  51.     LOS_IntRestore(intSave); 
  52.     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ì)列。

  1. LITE_OS_SEC_TEXT UINT32 LOS_QueueRead(UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeOut) 
  2.     UINT32 ret; 
  3.     UINT32 operateType; 
  4.  
  5. ⑴  ret = OsQueueReadParameterCheck(queueID, bufferAddr, &bufferSize, timeOut); 
  6.     if (ret != LOS_OK) { 
  7.         return ret; 
  8.     } 
  9.  
  10. ⑵  operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_READ, OS_QUEUE_HEAD, OS_QUEUE_POINT); 
  11.  
  12.     OsHookCall(LOS_HOOK_TYPE_QUEUE_READ, (LosQueueCB *)GET_QUEUE_HANDLE(queueID)); 
  13.  
  14.     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。

  1. UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeOut) 
  2.     LosQueueCB *queueCB = NULL
  3.     LosTaskCB *resumedTask = NULL
  4.     UINT32 ret; 
  5. ⑴  UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType); 
  6.     UINT32 readWriteTmp = !readWrite; 
  7.  
  8.     UINT32 intSave = LOS_IntLock(); 
  9.  
  10.     queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID); 
  11. ⑵  ret = OsQueueOperateParamCheck(queueCB, operateType, bufferSize); 
  12.     if (ret != LOS_OK) { 
  13.         goto QUEUE_END; 
  14.     } 
  15.  
  16. ⑶  if (queueCB->readWriteableCnt[readWrite] == 0) { 
  17.         if (timeOut == LOS_NO_WAIT) { 
  18.             ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL; 
  19.             goto QUEUE_END; 
  20.         } 
  21.  
  22.         if (g_losTaskLock) { 
  23.             ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK; 
  24.             goto QUEUE_END; 
  25.         } 
  26.  
  27.         LosTaskCB *runTsk = (LosTaskCB *)g_losTask.runTask; 
  28. ⑷      OsSchedTaskWait(&queueCB->readWriteList[readWrite], timeOut); 
  29.         LOS_IntRestore(intSave); 
  30.         LOS_Schedule(); 
  31.  
  32.         intSave = LOS_IntLock(); 
  33. ⑸      if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) { 
  34.             runTsk->taskStatus &= ~OS_TASK_STATUS_TIMEOUT; 
  35.             ret = LOS_ERRNO_QUEUE_TIMEOUT; 
  36.             goto QUEUE_END; 
  37.         } 
  38.     } else { 
  39. ⑹       queueCB->readWriteableCnt[readWrite]--; 
  40.     } 
  41.  
  42. ⑺   OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize); 
  43.  
  44.  
  45. ⑻  if (!LOS_ListEmpty(&queueCB->readWriteList[readWriteTmp])) { 
  46.         resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[readWriteTmp])); 
  47.         OsSchedTaskWake(resumedTask); 
  48.         LOS_IntRestore(intSave); 
  49.         LOS_Schedule(); 
  50.         return LOS_OK; 
  51.     } else { 
  52. ⑼      queueCB->readWriteableCnt[readWriteTmp]++; 
  53.     } 
  54.  
  55. QUEUE_END: 
  56.     LOS_IntRestore(intSave); 
  57.     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é)。

  1. static INLINE VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType, 
  2.                                                                 VOID *bufferAddr, UINT32 *bufferSize) 
  3.     UINT8 *queueNode = NULL
  4.     UINT32 msgDataSize; 
  5.     UINT16 queuePosion; 
  6.     errno_t rc; 
  7.  
  8.     /* get the queue position */ 
  9. ⑴  switch (OS_QUEUE_OPERATE_GET(operateType)) { 
  10.         case OS_QUEUE_READ_HEAD: 
  11. ⑵          queuePosion = queueCB->queueHead; 
  12.             ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++); 
  13.             break; 
  14.  
  15.         case OS_QUEUE_WRITE_HEAD: 
  16. ⑶          (queueCB->queueHead == 0) ? (queueCB->queueHead = (queueCB->queueLen - 1)) : (--queueCB->queueHead); 
  17.             queuePosion = queueCB->queueHead; 
  18.             break; 
  19.  
  20.         case OS_QUEUE_WRITE_TAIL: 
  21. ⑷          queuePosion = queueCB->queueTail; 
  22.             ((queueCB->queueTail + 1) == queueCB->queueLen) ? (queueCB->queueTail = 0) : (queueCB->queueTail++); 
  23.             break; 
  24.  
  25.         default
  26.             PRINT_ERR("invalid queue operate type!\n"); 
  27.             return
  28.     } 
  29.  
  30. ⑸  queueNode = &(queueCB->queue[(queuePosion * (queueCB->queueSize))]); 
  31.  
  32. ⑹  if (OS_QUEUE_IS_POINT(operateType)) { 
  33.       if (OS_QUEUE_IS_READ(operateType)) { 
  34.             *(UINT32 *)bufferAddr = *(UINT32 *)(VOID *)queueNode; 
  35.         } else { 
  36.             *(UINT32 *)(VOID *)queueNode = *(UINT32 *)bufferAddr;  // change to pp when calling OsQueueOperate 
  37.         } 
  38.     } else { 
  39. ⑺      if (OS_QUEUE_IS_READ(operateType)) { 
  40.             msgDataSize = *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32))); 
  41.             rc = memcpy_s((VOID *)bufferAddr, *bufferSize, (VOID *)queueNode, msgDataSize); 
  42.             if (rc != EOK) { 
  43.                 PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc); 
  44.                 return
  45.             } 
  46.  
  47.             *bufferSize = msgDataSize; 
  48.         } else { 
  49. ⑻          *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32))) = *bufferSize; 
  50.             rc = memcpy_s((VOID *)queueNode, queueCB->queueSize, (VOID *)bufferAddr, *bufferSize); 
  51.             if (rc != EOK) { 
  52.                 PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc); 
  53.                 return
  54.             } 
  55.         } 
  56.     } 

 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)建刪除、讀寫消息等。

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來(lái)源: 鴻蒙社區(qū)
相關(guān)推薦

2021-09-22 14:36:32

鴻蒙HarmonyOS應(yīng)用

2022-01-10 15:31:44

鴻蒙HarmonyOS應(yīng)用

2022-01-12 10:50:23

鴻蒙HarmonyOS應(yīng)用

2021-06-04 09:57:49

鴻蒙HarmonyOS應(yīng)用

2021-05-12 09:45:20

鴻蒙HarmonyOS應(yīng)用

2021-05-17 09:28:59

鴻蒙HarmonyOS應(yīng)用

2021-05-08 15:14:50

鴻蒙HarmonyOS應(yīng)用

2021-05-25 09:28:34

鴻蒙HarmonyOS應(yīng)用

2021-10-20 16:08:57

鴻蒙HarmonyOS應(yīng)用

2021-05-31 20:30:55

鴻蒙HarmonyOS應(yīng)用

2022-03-03 18:28:28

Harmony進(jìn)程任務(wù)管理模塊

2022-04-13 11:02:12

鴻蒙事件模塊事件Event

2021-07-06 09:45:03

鴻蒙HarmonyOS應(yīng)用

2022-03-11 20:23:14

鴻蒙源碼分析進(jìn)程管理

2021-06-09 09:48:01

鴻蒙HarmonyOS應(yīng)用

2021-05-21 09:25:11

鴻蒙HarmonyOS應(yīng)用

2021-05-27 09:43:56

鴻蒙HarmonyOS應(yīng)用

2021-05-11 09:54:55

鴻蒙HarmonyOS應(yīng)用

2022-01-14 08:39:47

鴻蒙HarmonyOS應(yīng)用

2021-05-10 15:05:56

鴻蒙HarmonyOS應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)