鴻蒙輕內(nèi)核M核源碼分析系列十三(續(xù)) 消息隊(duì)列QueueMail接口
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
之前分析過(guò)隊(duì)列(Queue)的源代碼,了解了隊(duì)列初始化、隊(duì)列創(chuàng)建、刪除、隊(duì)列讀取寫入等操作。隊(duì)列還提供了兩個(gè)接口OsQueueMailAlloc和OsQueueMailFree。隊(duì)列可以和一個(gè)靜態(tài)內(nèi)存池關(guān)聯(lián)起來(lái),一個(gè)任務(wù)從靜態(tài)內(nèi)存池申請(qǐng)內(nèi)存塊時(shí),如果申請(qǐng)不到,會(huì)把該任務(wù)插入到隊(duì)列的內(nèi)存阻塞鏈表中,等有其他任務(wù)釋放內(nèi)存時(shí),該任務(wù)會(huì)被分配內(nèi)存塊。
接下來(lái),詳細(xì)看下這2個(gè)接口的源代碼。
1、隊(duì)列結(jié)構(gòu)體定義
1.1 隊(duì)列結(jié)構(gòu)體定義
我們回憶下隊(duì)列結(jié)構(gòu)體的定義,在文件kernel\include\los_queue.h中定義隊(duì)列控制塊結(jié)構(gòu)體為L(zhǎng)osQueueCB,結(jié)構(gòu)體源代碼如下。需要看下成員變量memList,當(dāng)任務(wù)從和隊(duì)列關(guān)聯(lián)的靜態(tài)內(nèi)存池中申請(qǐng)不到空閑內(nèi)存塊時(shí),會(huì)把任務(wù)插入memList內(nèi)存阻塞鏈表,然后調(diào)度,進(jìn)行任務(wù)切換。等有其他任務(wù)釋放空閑內(nèi)存塊到這個(gè)靜態(tài)內(nèi)存池時(shí),該任務(wù)申請(qǐng)到空閑內(nèi)存塊,并把任務(wù)從memList內(nèi)存阻塞鏈表移除,插入到任務(wù)就緒隊(duì)列,并觸發(fā)任務(wù)調(diào)度。
- 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;
2、QueueMail接口源碼分析
2.1 OsQueueMailAlloc接口
我們可以使用函數(shù)VOID *OsQueueMailAlloc(UINT32 queueID, VOID *mailPool, UINT32 timeOut)從和隊(duì)列關(guān)聯(lián)的靜態(tài)內(nèi)存池中申請(qǐng)空閑內(nèi)存,下面通過(guò)分析源碼看看如何申請(qǐng)內(nèi)存。該函數(shù)需要3個(gè)參數(shù),queueID是一個(gè)在使用狀態(tài)的隊(duì)列的編號(hào),*mailPool是和隊(duì)列關(guān)聯(lián)的靜態(tài)內(nèi)存池地址,timeOut是超時(shí)時(shí)間,取值[0,LOS_WAIT_FOREVER]。該接口函數(shù)返回申請(qǐng)到的內(nèi)存地址或者NULL。
⑴處開始對(duì)參數(shù)進(jìn)行校驗(yàn),⑵處根據(jù)隊(duì)列編號(hào)獲取隊(duì)列控制結(jié)構(gòu)體queueCB,然后校驗(yàn)該隊(duì)列是否為使用狀態(tài)。⑶處調(diào)用靜態(tài)內(nèi)存分配函數(shù)LOS_MemboxAlloc獲取空閑內(nèi)存塊,然后獲取的內(nèi)存地址不為NULL,返回該內(nèi)存塊地址,否則執(zhí)行后續(xù)代碼。⑷處獲取當(dāng)前運(yùn)行的任務(wù)控制結(jié)構(gòu)體,⑸處把當(dāng)前任務(wù)加入隊(duì)列的內(nèi)存阻塞鏈表queueCB->memList,然后觸發(fā)任務(wù)調(diào)度。
等有其他其他任務(wù)調(diào)用OsQueueMailFree釋放內(nèi)存后,上述阻塞的任務(wù)獲得內(nèi)存塊,或者因超時(shí)退出阻塞列表并調(diào)度運(yùn)行后,會(huì)開始執(zhí)行⑹處語(yǔ)句。⑺處表示因?yàn)槌瑫r(shí)返回,任務(wù)沒有獲取到內(nèi)存塊,跳轉(zhuǎn)到END標(biāo)簽,返回NULL內(nèi)存地址。⑻處表示獲取到內(nèi)存塊,把任務(wù)的msg置空,并返回獲取到的內(nèi)存塊的地址。
- 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
我們可以使用函數(shù)UINT32 OsQueueMailFree(UINT32 queueID, VOID *mailPool, VOID *mailMem)釋放空閑內(nèi)存到和隊(duì)列關(guān)聯(lián)的靜態(tài)內(nèi)存池中,下面通過(guò)分析源碼看看如何釋放內(nèi)存。該函數(shù)需要3個(gè)參數(shù),queueID是一個(gè)在使用狀態(tài)的隊(duì)列的編號(hào),*mailPool是和隊(duì)列關(guān)聯(lián)的靜態(tài)內(nèi)存池地址,*mailMem表示要釋放的內(nèi)存塊地址。該接口返回值類型為無(wú)符號(hào)整數(shù),表示是否成功或者錯(cuò)誤碼。
⑴處開始對(duì)參數(shù)進(jìn)行校驗(yàn)。⑵處調(diào)用靜態(tài)內(nèi)存釋放函數(shù)LOS_MemboxFree釋放空閑內(nèi)存塊,如果釋放失敗,返回錯(cuò)誤碼。⑶處根據(jù)隊(duì)列編號(hào)獲取隊(duì)列控制結(jié)構(gòu)體queueCB,然后校驗(yàn)該隊(duì)列是否為使用狀態(tài)。成功釋放內(nèi)存后,如果隊(duì)列的內(nèi)存阻塞列表不為空,有阻塞任務(wù),則執(zhí)行⑷。⑸處從阻塞列表中獲取第一個(gè)任務(wù)控制結(jié)構(gòu)體,然后調(diào)用接口OsSchedTaskWake把任務(wù)從阻塞列表移除,并添加到任務(wù)就緒隊(duì)列。⑹處從靜態(tài)內(nèi)存池申請(qǐng)一個(gè)內(nèi)存塊,如果申請(qǐng)失敗返回錯(cuò)誤碼,否則執(zhí)行⑺,把申請(qǐng)到的內(nèi)存賦值到任務(wù)控制結(jié)構(gòu)體的msg成員變量,然后觸發(fā)調(diào)度。
- 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;
- }
小結(jié)
本文帶領(lǐng)大家一起剖析了鴻蒙輕內(nèi)核的隊(duì)列模塊的QueueMail兩個(gè)接口的源代碼。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)