乐趣区

关于队列:队列Queue任务间的消息读写安排起来

摘要: 本文通过剖析鸿蒙轻内核队列模块的源码,把握队列应用上的差别。

本文分享自华为云社区《鸿蒙轻内核 M 核源码剖析系列十三 音讯队列 Queue》,作者:zhushy。

队列(Queue)是一种罕用于工作间通信的数据结构。工作可能从队列外面读取音讯,当队列中的音讯为空时,挂起读取工作;当队列中有新音讯时,挂起的读取工作被唤醒并解决新音讯。工作也可能往队列里写入音讯,当队列曾经写满音讯时,挂起写入工作;当队列中有闲暇音讯节点时,挂起的写入工作被唤醒并写入音讯。如果将读队列和写队列的超时工夫设置为 0,则不会挂起工作,接口会间接返回,这就是非阻塞模式。音讯队列提供了异步解决机制,容许将一个音讯放入队列,但不立刻解决。同时队列还有缓冲音讯的作用。

本文通过剖析鸿蒙轻内核队列模块的源码,把握队列应用上的差别。本文中所波及的源码,以 OpenHarmony LiteOS- M 内核为例,均能够在开源站点 https://gitee.com/openharmony… 获取。

接下来,咱们看下队列的构造体,队列初始化,队列罕用操作的源代码。

1、队列构造体定义和罕用宏定义

1.1 队列构造体定义

在文件 kernel\include\los_queue.h 中定义队列管制块构造体为 LosQueueCB,构造体源代码如下。队列状态.queueState 取值 OS_QUEUE_UNUSED、OS_QUEUE_INUSED,其余构造体成员见正文局部。

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;

1.2 队列罕用宏定义

零碎反对创立多少队列是依据开发板状况应用宏 LOSCFG_BASE_IPC_QUEUE_LIMIT 定义的,每一个队列 queueID 是 queueID 类型的,取值为 [0,LOSCFG_BASE_IPC_QUEUE_LIMIT),示意队列池中各个队列的编号。

⑴处的宏从队列池中获取指定队列编号 QueueID 对应的队列管制块。⑵处依据双向链表节点 readWriteList[OS_QUEUE_WRITE] 获取队列管制块内存地址。

⑴    #define GET_QUEUE_HANDLE(QueueID) (((LosQueueCB *)g_allQueue) + (QueueID))

⑵    #define GET_QUEUE_LIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosQueueCB, readWriteList[OS_QUEUE_WRITE])

另外,队列中还提供了比拟重要的队列读取音讯操作相干的枚举和宏。枚举 QueueReadWrite 辨别队列的读和写,枚举 QueueHeadTail 辨别队列的首和尾,枚举 QueuePointOrNot 辨别读写音讯时是应用值还是指针。

队列的操作类型应用 3 比特位的数字来示意,见宏 OS_QUEUE_OPERATE_TYPE 的定义,其中高 1 位示意读写数值还是读写指针地址,中 1 位示意队首还是队尾,低 1 位示意读取还是写入。枚举和宏的定义如下:

typedef enum {
    OS_QUEUE_READ,
    OS_QUEUE_WRITE
} QueueReadWrite;

typedef enum {
    OS_QUEUE_HEAD,
    OS_QUEUE_TAIL
} QueueHeadTail;

typedef enum {
    OS_QUEUE_NOT_POINT,
    OS_QUEUE_POINT
} QueuePointOrNot;

#define OS_QUEUE_OPERATE_TYPE(ReadOrWrite, HeadOrTail, PointOrNot)  \
                (((UINT32)(PointOrNot) << 2) | ((UINT32)(HeadOrTail) << 1) | (ReadOrWrite))
#define OS_QUEUE_READ_WRITE_GET(type) ((type) & (0x01))
#define OS_QUEUE_READ_HEAD     (OS_QUEUE_READ | (OS_QUEUE_HEAD << 1))
#define OS_QUEUE_READ_TAIL     (OS_QUEUE_READ | (OS_QUEUE_TAIL << 1))
#define OS_QUEUE_WRITE_HEAD    (OS_QUEUE_WRITE | (OS_QUEUE_HEAD << 1))
#define OS_QUEUE_WRITE_TAIL    (OS_QUEUE_WRITE | (OS_QUEUE_TAIL << 1))
#define OS_QUEUE_OPERATE_GET(type) ((type) & (0x03))
#define OS_QUEUE_IS_POINT(type)    ((type) & (0x04))
#define OS_QUEUE_IS_READ(type)     (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_READ)
#define OS_QUEUE_IS_WRITE(type)    (OS_QUEUE_READ_WRITE_GET(type) == OS_QUEUE_WRITE)
#define OS_READWRITE_LEN           2

2、队列初始化

队列在内核中默认开启,用户能够通过宏 LOSCFG_BASE_IPC_QUEUE 进行敞开。开启队列的状况下,在系统启动时,在 kernel\src\los_init.c 中调用 OsQueueInit() 进行队列模块初始化。上面,咱们剖析下队列初始化的代码。

⑴为队列申请内存,如果申请失败,则返回谬误。⑵初始化双向循环链表 g_freeQueueList,保护未应用的队列。⑶循环每一个队列进行初始化,为每一个队列节点指定索引 queueID,并把队列节点插入未应用队列双向链表 g_freeQueueList。代码上能够看出,挂在未应用队列双向链表上的节点是每个队列管制块的写阻塞工作链表节点.readWriteList[OS_QUEUE_WRITE]。

LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID)
{
    LosQueueCB *queueNode = NULL;
    UINT16 index;

    if (LOSCFG_BASE_IPC_QUEUE_LIMIT == 0) {return LOS_ERRNO_QUEUE_MAXNUM_ZERO;}

⑴  g_allQueue = (LosQueueCB *)LOS_MemAlloc(m_aucSysMem0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB));
    if (g_allQueue == NULL) {return LOS_ERRNO_QUEUE_NO_MEMORY;}

    (VOID)memset_s(g_allQueue, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB),
                   0, LOSCFG_BASE_IPC_QUEUE_LIMIT * sizeof(LosQueueCB));

⑵  LOS_ListInit(&g_freeQueueList);
⑶  for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {queueNode = ((LosQueueCB *)g_allQueue) + index;
        queueNode->queueID = index;
        LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);
    }

    return LOS_OK;
}

3、队列罕用操作

3.1 队列创立

创立队列函数是 LOS_QueueCreate(),先看看该函数的参数:queueName 是队列名称,实际上并没有应用。len 是队列中音讯的数量,queueID 是队列编号,flags 保留未应用。maxMsgSize 是队列中每条音讯的最大大小。

咱们剖析下创立队列的代码。⑴处对参数进行校验,队列编码不能为空,队列音讯长度不能太大,队列音讯数量和队列音讯大小不能为 0。⑵处计算音讯的理论最大大小 msgSize,即 maxMsgSize + sizeof(UINT32) 音讯最大大小再加 4 个字节,在音讯的最初 4 个字节用来保留音讯的理论长度。而后调用⑶处函数 LOS_MemAlloc() 为对队列动静申请内存,如果内存申请失败,则返回错误码。

⑷处判断 g_freeQueueList 是否为空,如果没有能够应用的队列,开释前文申请的内存。⑸处如果 g_freeQueueList 不为空,则获取第一个可用的队列节点,接着从双向链表 g_freeQueueList 中删除,而后调用宏 GET_QUEUE_LIST 获取 LosQueueCB *queueCB,初始化创立的队列信息,蕴含队列的长度.queueLen、音讯大小.queueSize,队列内存空间.queue,音讯状态.queueState,可读的数量.readWriteableCnt[OS_QUEUE_READ] 为 0,可写的数量 readWriteableCnt[OS_QUEUE_WRITE] 为队列音讯长度 len,队列头地位.queueHead 和尾地位.queueTail 为 0。

⑹初始化双向链表.readWriteList[OS_QUEUE_READ],阻塞在这个队列上的读音讯工作会挂在这个链表上。初始化双向链表.readWriteList[OS_QUEUE_WRITE],阻塞在这个队列上的写音讯工作会挂在这个链表上。初始化双向链表.memList。⑺赋值给输入参数 *queueID,后续程序应用这个队列编号对队列进行其余操作。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName,
                                             UINT16 len,
                                             UINT32 *queueID,
                                             UINT32 flags,
                                             UINT16 maxMsgSize)
{
    LosQueueCB *queueCB = NULL;
    UINT32 intSave;
    LOS_DL_LIST *unusedQueue = NULL;
    UINT8 *queue = NULL;
    UINT16 msgSize;

    (VOID)queueName;
    (VOID)flags;

⑴  if (queueID == NULL) {return LOS_ERRNO_QUEUE_CREAT_PTR_NULL;}

    if (maxMsgSize > (OS_NULL_SHORT - sizeof(UINT32))) {return LOS_ERRNO_QUEUE_SIZE_TOO_BIG;}

    if ((len == 0) || (maxMsgSize == 0)) {return LOS_ERRNO_QUEUE_PARA_ISZERO;}
⑵  msgSize = maxMsgSize + sizeof(UINT32);

    /* Memory allocation is time-consuming, to shorten the time of disable interrupt,
       move the memory allocation to here. */
⑶  queue = (UINT8 *)LOS_MemAlloc(m_aucSysMem0, len * msgSize);
    if (queue == NULL) {return LOS_ERRNO_QUEUE_CREATE_NO_MEMORY;}

    intSave = LOS_IntLock();
⑷  if (LOS_ListEmpty(&g_freeQueueList)) {LOS_IntRestore(intSave);
        (VOID)LOS_MemFree(m_aucSysMem0, queue);
        return LOS_ERRNO_QUEUE_CB_UNAVAILABLE;
    }

⑸  unusedQueue = LOS_DL_LIST_FIRST(&(g_freeQueueList));
    LOS_ListDelete(unusedQueue);
    queueCB = (GET_QUEUE_LIST(unusedQueue));
    queueCB->queueLen = len;
    queueCB->queueSize = msgSize;
    queueCB->queue = queue;
    queueCB->queueState = OS_QUEUE_INUSED;
    queueCB->readWriteableCnt[OS_QUEUE_READ] = 0;
    queueCB->readWriteableCnt[OS_QUEUE_WRITE] = len;
    queueCB->queueHead = 0;
    queueCB->queueTail = 0;
⑹  LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]);
    LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]);
    LOS_ListInit(&queueCB->memList);
    LOS_IntRestore(intSave);

⑺  *queueID = queueCB->queueID;

    OsHookCall(LOS_HOOK_TYPE_QUEUE_CREATE, queueCB);

    return LOS_OK;
}

3.2 队列删除

咱们能够应用函数 LOS_QueueDelete(UINT32 queueID) 来删除队列,上面通过剖析源码看看如何删除队列的。

⑴处判断队列 queueID 是否超过 LOSCFG_BASE_IPC_QUEUE_LIMIT,如果超过则返回错误码。如果队列编号没有问题,获取队列管制块 LosQueueCB *queueCB。⑵处判断要删除的队列处于未应用状态,则跳转到谬误标签 QUEUE_END 进行解决。⑶如果队列的阻塞读、阻塞写工作列表不为空,或内存节点链表不为空,则不容许删除,跳转到谬误标签进行解决。⑷处测验队列的可读、可写数量是否出错。

⑸处应用指针 UINT8 *queue 保留队列的内存空间,⑹处把.queue 置空,把.queueState 设置为未应用 OS_QUEUE_UNUSED,并把队列节点插入未应用队列双向链表 g_freeQueueList。接下来会须要调用⑺处函数 LOS_MemFree() 开释队列内存空间。

LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID)
{
    LosQueueCB *queueCB = NULL;
    UINT8 *queue = NULL;
    UINT32 intSave;
    UINT32 ret;

⑴  if (queueID >= LOSCFG_BASE_IPC_QUEUE_LIMIT) {return LOS_ERRNO_QUEUE_NOT_FOUND;}

    intSave = LOS_IntLock();
    queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);
⑵  if (queueCB->queueState == OS_QUEUE_UNUSED) {
        ret = LOS_ERRNO_QUEUE_NOT_CREATE;
        goto QUEUE_END;
    }

⑶  if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_READ])) {
        ret = LOS_ERRNO_QUEUE_IN_TSKUSE;
        goto QUEUE_END;
    }

    if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_WRITE])) {
        ret = LOS_ERRNO_QUEUE_IN_TSKUSE;
        goto QUEUE_END;
    }

    if (!LOS_ListEmpty(&queueCB->memList)) {
        ret = LOS_ERRNO_QUEUE_IN_TSKUSE;
        goto QUEUE_END;
    }

⑷  if ((queueCB->readWriteableCnt[OS_QUEUE_WRITE] + queueCB->readWriteableCnt[OS_QUEUE_READ]) !=
        queueCB->queueLen) {
        ret = LOS_ERRNO_QUEUE_IN_TSKWRITE;
        goto QUEUE_END;
    }

⑸  queue = queueCB->queue;
⑹  queueCB->queue = (UINT8 *)NULL;
    queueCB->queueState = OS_QUEUE_UNUSED;
    LOS_ListAdd(&g_freeQueueList, &queueCB->readWriteList[OS_QUEUE_WRITE]);
    LOS_IntRestore(intSave);

    OsHookCall(LOS_HOOK_TYPE_QUEUE_DELETE, queueCB);

⑺  ret = LOS_MemFree(m_aucSysMem0, (VOID *)queue);
    return ret;

QUEUE_END:
    LOS_IntRestore(intSave);
    return ret;
}

上面就来看看队列的读写,有 2 点须要留神:

  • 队首、队尾的读写

只反对队首读取,不能队尾读取,否则就不算队列了。除了失常的队尾写音讯外,还提供插队机制,反对从队首写入。

  • 队列音讯数据内容

往队列中写入的音讯的类型有 2 种,即反对按地址写入和按值写入(带拷贝)。按哪种类型写入,就须要配对的按相应的类型去读取。

队列读取接口的类别,归纳如下:

3.3 队列读取

咱们晓得有 2 个队列读取办法,按指针地址读取的函数 LOS_QueueRead() 和按音讯数值读取的函数 LOS_QueueReadCopy()。咱们先看下函数 LOS_QueueRead(),该函数的参数有 4 个,队列编号 queueID,寄存读取到的音讯的缓冲区地址 *bufferAddr,寄存读取到的音讯的缓冲区大小 bufferSize,读队列音讯的期待超时工夫 timeOut。代码如下,咱们剖析下代码。

⑴处校验传入参数,队列编号不能超出限度,传入的指针不能为空,缓冲大小不能为 0。如果 timeout 不为零,不能在中断中读取队列。⑵处操作类型示意队首读取音讯指针,而后调用函数 OsQueueOperate() 进一步操作队列。

LITE_OS_SEC_TEXT UINT32 LOS_QueueRead(UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeOut)
{
    UINT32 ret;
    UINT32 operateType;

⑴  ret = OsQueueReadParameterCheck(queueID, bufferAddr, &bufferSize, timeOut);
    if (ret != LOS_OK) {return ret;}

⑵  operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_READ, OS_QUEUE_HEAD, OS_QUEUE_POINT);

    OsHookCall(LOS_HOOK_TYPE_QUEUE_READ, (LosQueueCB *)GET_QUEUE_HANDLE(queueID));

    return OsQueueOperate(queueID, operateType, bufferAddr, &bufferSize, timeOut);
}

咱们进一步剖析下函数 OsQueueOperate(),这是是比拟通用的封装,读取,写入都会调用这个函数,咱们以读取队列为例剖析这个函数。⑴处获取队列的操作类型,为读取操作。⑵处先调用函数 OsQueueOperateParamCheck() 进行参数校验,校验队列是应用中的队列,并对读写音讯大小进行校验。⑶处如果可读数量为 0,无奈读取时,如果是零期待则返回错误码。如果以后锁任务调度,跳出函数执行。否则,执行⑷把当前任务放入队列的读取音讯阻塞队列,而后触发任务调度,后续的代码临时不再执行。如果可读的数量不为 0,能够持续读取时,执行⑹处代码把可读数量减 1,而后继续执行⑺处代码读取队列。

等读取队列阻塞超时,或者队列能够读取后,继续执行⑸处的代码。如果是产生超时,队列还不能读取,更改工作状态,跳出函数执行。如果队列能够读取了,继续执行⑺处代码读取队列。⑻处在胜利读取队列后,如果有工作阻塞在写入队列,则获取阻塞链表中的第一个工作 resumedTask,而后调用唤醒函数 OsSchedTaskWake() 把待复原的工作放入就绪队列,触发一次任务调度。如果无阻塞工作,则把可写入的数量加 1。

UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeOut)
{
    LosQueueCB *queueCB = NULL;
    LosTaskCB *resumedTask = NULL;
    UINT32 ret;
⑴  UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);
    UINT32 readWriteTmp = !readWrite;

    UINT32 intSave = LOS_IntLock();

    queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);
⑵  ret = OsQueueOperateParamCheck(queueCB, operateType, bufferSize);
    if (ret != LOS_OK) {goto QUEUE_END;}

⑶  if (queueCB->readWriteableCnt[readWrite] == 0) {if (timeOut == LOS_NO_WAIT) {ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL;
            goto QUEUE_END;
        }

        if (g_losTaskLock) {
            ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK;
            goto QUEUE_END;
        }

        LosTaskCB *runTsk = (LosTaskCB *)g_losTask.runTask;
⑷      OsSchedTaskWait(&queueCB->readWriteList[readWrite], timeOut);
        LOS_IntRestore(intSave);
        LOS_Schedule();

        intSave = LOS_IntLock();
⑸      if (runTsk->taskStatus & OS_TASK_STATUS_TIMEOUT) {
            runTsk->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;
            ret = LOS_ERRNO_QUEUE_TIMEOUT;
            goto QUEUE_END;
        }
    } else {⑹       queueCB->readWriteableCnt[readWrite]--;
    }

⑺   OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize);
⑻  if (!LOS_ListEmpty(&queueCB->readWriteList[readWriteTmp])) {resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[readWriteTmp]));
        OsSchedTaskWake(resumedTask);
        LOS_IntRestore(intSave);
        LOS_Schedule();
        return LOS_OK;
    } else {⑼      queueCB->readWriteableCnt[readWriteTmp]++;
    }

QUEUE_END:
    LOS_IntRestore(intSave);
    return ret;
}

咱们再持续看下函数 OsQueueBufferOperate() 是具体如何读取队列的。⑴处 switch-case 语句依据操作类型获取操作地位。对于⑵头部读取的状况,先获取读取地位 queuePosition。而后,如果以后头节点地位.queueHead 加 1 等于队列音讯长度,头节点地位.queueHead 设置为 0,否则加 1。对于⑶头部写入的状况,如果以后头节点地位.queueHead 等于 0,头节点地位.queueHead 设置为队列音讯长度减 1 即 queueCB->queueLen – 1,否则头节点地位.queueHead 减 1 即可。而后,获取要写入的地位 queuePosition。对于⑷尾部写入的状况,先获取写入地位 queuePosition。而后,如果以后尾节点地位.queueTail 加 1 等于队列音讯长度,尾节点地位.queueTail 设置为 0,否则加 1。

⑸处基于获取的队列读取地位获取队列音讯节点 queueNode。⑹处判断操作类型如果是按指针读写音讯,间接读取音讯节点的数据写入指针对应的缓冲区 (UINT32 )bufferAddr,或间接把指针对应的缓冲区 (UINT32 )bufferAddr 数据写入音讯节点即可。咱们接着看如何按数数据读写音讯,⑺处代码用于读取数据音讯。每个音讯节点的后 4 个字节保留的是音讯的长度,首先获取音讯的长度 msgDataSize,而后把音讯内容读取到 bufferAddr。再看看⑻处如何写入队列音讯,首先把音讯内容写入到 queueNode,而后再把音讯长度的内容写入到 queueNode + queueCB->queueSize – sizeof(UINT32),就是每个音讯节点的后 4 字节。

static INLINE VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType,
                                                                VOID *bufferAddr, UINT32 *bufferSize)
{
    UINT8 *queueNode = NULL;
    UINT32 msgDataSize;
    UINT16 queuePosion;
    errno_t rc;

    /* get the queue position */
⑴  switch (OS_QUEUE_OPERATE_GET(operateType)) {
        case OS_QUEUE_READ_HEAD:
⑵          queuePosion = queueCB->queueHead;
            ((queueCB->queueHead + 1) == queueCB->queueLen) ? (queueCB->queueHead = 0) : (queueCB->queueHead++);
            break;

        case OS_QUEUE_WRITE_HEAD:
⑶          (queueCB->queueHead == 0) ? (queueCB->queueHead = (queueCB->queueLen - 1)) : (--queueCB->queueHead);
            queuePosion = queueCB->queueHead;
            break;

        case OS_QUEUE_WRITE_TAIL:
⑷          queuePosion = queueCB->queueTail;
            ((queueCB->queueTail + 1) == queueCB->queueLen) ? (queueCB->queueTail = 0) : (queueCB->queueTail++);
            break;

        default:
            PRINT_ERR("invalid queue operate type!\n");
            return;
    }

⑸  queueNode = &(queueCB->queue[(queuePosion * (queueCB->queueSize))]);

⑹  if (OS_QUEUE_IS_POINT(operateType)) {if (OS_QUEUE_IS_READ(operateType)) {*(UINT32 *)bufferAddr = *(UINT32 *)(VOID *)queueNode;
        } else {*(UINT32 *)(VOID *)queueNode = *(UINT32 *)bufferAddr;  // change to pp when calling OsQueueOperate
        }
    } else {⑺      if (OS_QUEUE_IS_READ(operateType)) {msgDataSize = *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32)));
            rc = memcpy_s((VOID *)bufferAddr, *bufferSize, (VOID *)queueNode, msgDataSize);
            if (rc != EOK) {PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc);
                return;
            }

            *bufferSize = msgDataSize;
        } else {⑻          *((UINT32 *)(UINTPTR)((queueNode + queueCB->queueSize) - sizeof(UINT32))) = *bufferSize;
            rc = memcpy_s((VOID *)queueNode, queueCB->queueSize, (VOID *)bufferAddr, *bufferSize);
            if (rc != EOK) {PRINT_ERR("%s[%d] memcpy failed, error type = %u\n", __FUNCTION__, __LINE__, rc);
                return;
            }
        }
    }
}

3.4 队列写入

咱们晓得,有 4 个队列写入办法,2 个队尾写入,2 个队首写入,别离蕴含按指针地址写入音讯和按数值写入音讯。LOS_QueueWrite() 会调用 LOS_QueueWriteCopy(),LOS_QueueWriteHead() 会调用 LOS_QueueWriteHeadCopy(),而后指定不同的操作类型后,会进一步调用前文曾经剖析过的函数 OsQueueOperate()。

小结

本文率领大家一起分析了鸿蒙轻内核的队列模块的源代码,蕴含队列的构造体、队列池初始化、队列创立删除、读写音讯等。感激浏览,如有任何问题、倡议,都能够留言给咱们:https://gitee.com/openharmony…。为了更容易找到鸿蒙轻内核代码仓,倡议拜访 https://gitee.com/openharmony…,关注 Watch、点赞 Star、并 Fork 到本人账户下,谢谢。

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

退出移动版