关于harmonyos:解析鸿蒙内核消息队列QueueMail接口的哼哈二将

2次阅读

共计 3996 个字符,预计需要花费 10 分钟才能阅读完成。

摘要: 本文率领大家一起分析了鸿蒙轻内核的队列模块的 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 两个接口的源代码。感激浏览,如有任何问题、倡议,都能够留言给我,谢谢。

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0