解析鴻蒙核心訊息佇列QueueMail介面的哼哈二將

此賬號為華為雲開發者社群官方運營賬號,提供全面深入的雲計算前景分析、豐富的技術乾貨、程式樣例,分享華為雲前沿資訊動態

本文分享自華為雲社群《鴻蒙輕核心M核原始碼分析系列十三(續) 訊息佇列QueueMail介面》,作者:zhushy 。

之前分析過佇列(Queue)的原始碼,瞭解了佇列初始化、佇列建立、刪除、佇列讀取寫入等操作。佇列還提供了兩個介面OsQueueMailAlloc和OsQueueMailFree。佇列可以和一個靜態記憶體池關聯起來,一個任務從靜態記憶體池申請記憶體塊時,如果申請不到,會把該任務插入到佇列的記憶體阻塞連結串列中,等有其他任務釋放記憶體時,該任務會被分配記憶體塊。

接下來,詳細看下這2個介面的原始碼。

1、佇列結構體定義

我們回憶下佇列結構體的定義,在檔案kernel\include\los_queue。h中定義佇列控制塊結構體為LosQueueCB,結構體原始碼如下。需要看下成員變數memList,當任務從和佇列關聯的靜態記憶體池中申請不到空閒記憶體塊時,會把任務插入memList記憶體阻塞連結串列,然後排程,進行任務切換。等有其他任務釋放空閒記憶體塊到這個靜態記憶體池時,該任務申請到空閒記憶體塊,並把任務從memList記憶體阻塞連結串列移除,插入到任務就緒佇列,並觸發任務排程。

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;

2、QueueMail介面原始碼分析

2。1 OsQueueMailAlloc介面

我們可以使用函式VOID*OsQueueMailAlloc(UINT32 queueID, VOID *mailPool, UINT32 timeOut)從和佇列關聯的靜態記憶體池中申請空閒記憶體,下面透過分析原始碼看看如何申請記憶體。該函式需要3個引數,queueID是一個在使用狀態的佇列的編號,*mailPool是和佇列關聯的靜態記憶體池地址,timeOut是超時時間,取值[0,LOS_WAIT_FOREVER]。該介面函式返回申請到的記憶體地址或者NULL。

⑴處開始對引數進行校驗,⑵處根據佇列編號獲取佇列控制結構體queueCB,然後校驗該佇列是否為使用狀態。⑶處呼叫靜態記憶體分配函式LOS_MemboxAlloc獲取空閒記憶體塊,然後獲取的記憶體地址不為NULL,返回該記憶體塊地址,否則執行後續程式碼。⑷處獲取當前執行的任務控制結構體,⑸處把當前任務加入佇列的記憶體阻塞連結串列queueCB->memList,然後觸發任務排程。

等有其他其他任務呼叫OsQueueMailFree釋放記憶體後,上述阻塞的任務獲得記憶體塊,或者因超時退出阻塞列表並排程執行後,會開始執行⑹處語句。⑺處表示因為超時返回,任務沒有獲取到記憶體塊,跳轉到END標籤,返回NULL記憶體地址。⑻處表示獲取到記憶體塊,把任務的msg置空,並返回獲取到的記憶體塊的地址。

LITE_OS_SEC_TEXT VOID *OsQueueMailAlloc(UINT32 queueID, VOID *mailPool, UINT32 timeOut){ VOID *mem = (VOID *)NULL; UINT32 intSave; LosQueueCB *queueCB = (LosQueueCB *)NULL; LosTaskCB *runTsk = (LosTaskCB *)NULL;⑴ if (queueID >= LOSCFG_BASE_IPC_QUEUE_LIMIT) { return NULL; } if (mailPool == NULL) { return NULL; } if (timeOut != LOS_NO_WAIT) { if (OS_INT_ACTIVE) { return NULL; } } intSave = LOS_IntLock();⑵ queueCB = GET_QUEUE_HANDLE(queueID); if (queueCB->queueState == OS_QUEUE_UNUSED) { goto END; }⑶ mem = LOS_MemboxAlloc(mailPool); if (mem == NULL) { if (timeOut == LOS_NO_WAIT) { goto END; }⑷ runTsk = (LosTaskCB *)g_losTask。runTask;⑸ OsSchedTaskWait(&queueCB->memList, timeOut); LOS_IntRestore(intSave); LOS_Schedule();⑹ intSave = LOS_IntLock(); if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) {⑺ runTsk->taskStatus &= (~OS_TASK_STATUS_TIMEOUT); goto END; } else { /* When enters the current branch, means the current task already got a available membox, * so the runTsk->msg can not be NULL。 */⑻ mem = runTsk->msg; runTsk->msg = NULL; } }END: LOS_IntRestore(intSave); return mem;}

2。2 OsQueueMailFree

我們可以使用函式UINT32OsQueueMailFree(UINT32 queueID, VOID *mailPool, VOID *mailMem)釋放空閒記憶體到和佇列關聯的靜態記憶體池中,下面透過分析原始碼看看如何釋放記憶體。該函式需要3個引數,queueID是一個在使用狀態的佇列的編號,*mailPool是和佇列關聯的靜態記憶體池地址,*mailMem表示要釋放的記憶體塊地址。該介面返回值型別為無符號整數,表示是否成功或者錯誤碼。

⑴處開始對引數進行校驗。⑵處呼叫靜態記憶體釋放函式LOS_MemboxFree釋放空閒記憶體塊,如果釋放失敗,返回錯誤碼。⑶處根據佇列編號獲取佇列控制結構體queueCB,然後校驗該佇列是否為使用狀態。成功釋放記憶體後,如果佇列的記憶體阻塞列表不為空,有阻塞任務,則執行⑷。⑸處從阻塞列表中獲取第一個任務控制結構體,然後呼叫介面OsSchedTaskWake把任務從阻塞列表移除,並新增到任務就緒佇列。⑹處從靜態記憶體池申請一個記憶體塊,如果申請失敗返回錯誤碼,否則執行⑺,把申請到的記憶體賦值到任務控制結構體的msg成員變數,然後觸發排程。

LITE_OS_SEC_TEXT UINT32 OsQueueMailFree(UINT32 queueID, VOID *mailPool, VOID *mailMem){ VOID *mem = (VOID *)NULL; UINT32 intSave; LosQueueCB *queueCB = (LosQueueCB *)NULL; LosTaskCB *resumedTask = (LosTaskCB *)NULL;⑴ if (queueID >= LOSCFG_BASE_IPC_QUEUE_LIMIT) { return LOS_ERRNO_QUEUE_MAIL_HANDLE_INVALID; } if (mailPool == NULL) { return LOS_ERRNO_QUEUE_MAIL_PTR_INVALID; } intSave = LOS_IntLock();⑵ if (LOS_MemboxFree(mailPool, mailMem)) { LOS_IntRestore(intSave); return LOS_ERRNO_QUEUE_MAIL_FREE_ERROR; }⑶ queueCB = GET_QUEUE_HANDLE(queueID); if (queueCB->queueState == OS_QUEUE_UNUSED) { LOS_IntRestore(intSave); return LOS_ERRNO_QUEUE_NOT_CREATE; }⑷ if (!LOS_ListEmpty(&queueCB->memList)) {⑸ resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->memList)); OsSchedTaskWake(resumedTask);⑹ mem = LOS_MemboxAlloc(mailPool); if (mem == NULL) { LOS_IntRestore(intSave); return LOS_ERRNO_QUEUE_NO_MEMORY; }⑺ resumedTask->msg = mem; LOS_IntRestore(intSave); LOS_Schedule(); } else { LOS_IntRestore(intSave); } return LOS_OK;}

小結

本文帶領大家一起剖析了鴻蒙輕核心的佇列模組的QueueMail兩個介面的原始碼。感謝閱讀,如有任何問題、建議,都可以留言給我,謝謝。

點選關注,第一時間瞭解華為雲新鮮技術~

華為雲部落格_大資料部落格_AI部落格_雲計算部落格_開發者中心-華為雲