解析鴻蒙內核消息隊列QueueMail接口的哼哈二將


摘要:本文帶領大家一起剖析了鴻蒙輕內核的隊列模塊的QueueMail兩個接口的源代碼。

本文分享自華為雲社區《鴻蒙輕內核M核源碼分析系列十三(續) 消息隊列QueueMail接口》,作者:zhushy 。

之前分析過隊列(Queue)的源代碼,了解了隊列初始化、隊列創建、刪除、隊列讀取寫入等操作。隊列還提供了兩個接口OsQueueMailAlloc和OsQueueMailFree。隊列可以和一個靜態內存池關聯起來,一個任務從靜態內存池申請內存塊時,如果申請不到,會把該任務插入到隊列的內存阻塞鏈表中,等有其他任務釋放內存時,該任務會被分配內存塊。

接下來,詳細看下這2個接口的源代碼。

1、隊列結構體定義

1.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

我們可以使用函數UINT32 OsQueueMailFree(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兩個接口的源代碼。感謝閱讀,如有任何問題、建議,都可以留言給我,謝謝。

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM