FreeRTOS内核源码之消息队列

FreeRTOS内核实现与应用开发实战指南

P238 通用消息队列发送函数 xQueueGenericSend()(任务)

上面看到的那些在任务中发送消息的函数都是 xQueueGenericSend()展开的宏定义,真

正起作用的就是 xQueueGenericSend()函数,根据指定的参数不一样,发送消息的结果就不

一样,下面一起看看任务级的通用消息队列发送函数的实现过程,具体见代码清单 17-18。

代码清单 17-18 xQueueGenericSend()函数源码(已删减)

BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition)
{
    BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
    TimeOut_t xTimeOut;
    Queue_t *const pxQueue = (Queue_t *)xQueue;

    /* 已删除一些断言操作 */

    for (;;)
    {
        taskENTER_CRITICAL();/*进入临界段*/
        {
            /* 队列未满 */
            if ((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE))
            {
                traceQUEUE_SEND(pxQueue);
                xYieldRequired =
                    prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);

                /* 已删除使用队列集部分代码 */
                /* 如果有任务在等待获取此消息队列 */
                if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
                {
                    /* 将任务从阻塞中恢复 */
                    if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
                    {
                        /* 如果恢复的任务优先级比当前运行任务优先级还高,
                         * 那么需要进行一次任务切换 */
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else if (xYieldRequired != pdFALSE)
                {
                    /* 如果没有等待的任务,拷贝成功也需要任务切换 */
                    queueYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            /* 队列已满 */
            else
            {
                if (xTicksToWait == (TickType_t)0)
                {
                    /* 如果用户不指定阻塞超时时间,退出 */
                    taskEXIT_CRITICAL();
                    traceQUEUE_SEND_FAILED(pxQueue);
                    return errQUEUE_FULL;
                }
                else if (xEntryTimeSet == pdFALSE)
                {
                    /* 初始化阻塞超时结构体变量,初始化进入
                     * 阻塞的时间 xTickCount 和溢出次数 xNumOfOverflows */
                    vTaskSetTimeOutState(&xTimeOut);
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();
        /* 挂起调度器 */
        vTaskSuspendAll();
        /* 队列上锁 */
        prvLockQueue(pxQueue);

        /* 检查超时时间是否已经过去了 */
        if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
        {
            /* 如果队列还是满的 */
            if (prvIsQueueFull(pxQueue) != pdFALSE)
            {
                traceBLOCKING_ON_QUEUE_SEND(pxQueue);
                /* 将当前任务添加到队列的等待发送列表中
                 * 以及阻塞延时列表,延时时间为用户指定的超时时间 xTicksToWait */
                vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);
                /* 队列解锁 */
                prvUnlockQueue(pxQueue);

                /* 恢复调度器 */
                if (xTaskResumeAll() == pdFALSE)
                {
                    portYIELD_WITHIN_API();
                }
            }
            else
            {

                /* 队列有空闲消息空间,允许入队 */
                prvUnlockQueue(pxQueue);
                (void)xTaskResumeAll();
            }
        }
        else
        {
            /* 超时时间已过,退出 */
            prvUnlockQueue(pxQueue);
            (void)xTaskResumeAll();

            traceQUEUE_SEND_FAILED(pxQueue);
            return errQUEUE_FULL;
        }
    }
}
/*-----------------------------------------------------------*/

/*
总结:
调用写队列函数时,
如果队列不满,发送信息,且对面没有读任务,立刻切换任务,返回
如果队列不满,发送信息,且对面有读任务,唤醒读任务,立刻切换任务,返回(若没有更高优先级的任务,读任务得到执行)
如果队列满,且指定阻塞时间为0,立刻返回。
如果队列满,且指定阻塞时间不为0,且时间未过,进入阻塞,等待被列表或定时器唤醒。
如果队列满,且指定阻塞时间不为0,且时间过了,立刻返回错误代码。
*/
 /*
 总结:
 调用写队列函数时,
 如果队列不满,发送信息,且对面没有读任务,立刻切换任务,返回
 如果队列不满,发送信息,且对面有读任务,唤醒读任务,立刻切换任务,返回(若没有更高优先级的任务,读任务得到执行)
 如果队列满,且指定阻塞时间为0,立刻返回。
 如果队列满,且指定阻塞时间不为0,且时间未过,进入阻塞,等待被读队列函数或时间片中断(阻塞时间过了)唤醒。
 如果队列满,且指定阻塞时间不为0,且时间过了,立刻返回错误代码。
 */
P242 消息队列发送函数 xQueueGenericSendFromISR()(中断)

既然有任务中发送消息的函数,当然也需要有在中断中发送消息函数,其实这个函数跟 xQueueGenericSend() 函 数 很 像 , 只 不 过 是 执 行 的 上 下 文 环 境 是 不 一 样 的 ,xQueueGenericSendFromISR()函数只能用于中断中执行,是不带阻塞机制的,源码具体见 代码清单 17-19。

代码清单 17-19xQueueGenericSendFromISR()函数源码

BaseType_t xQueueGenericSendFromISR(QueueHandle_t xQueue,
                                    const void *const pvItemToQueue,
                                    BaseType_t *const xHigherPriorityTaskWoken,
                                    const BaseType_t xCopyPosition)
{
    BaseType_t xReturn;
    UBaseType_t uxSavedInterruptStatus;
    Queue_t *const pxQueue = (Queue_t *)xQueue;

    uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        /* 队列未满 */
        if ((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE))
        {
            const int8_t cTxLock = pxQueue->cTxLock;
            traceQUEUE_SEND_FROM_ISR(pxQueue);

            /* 完成消息拷贝 */
            (void)prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);

            /* 判断队列是否上锁 */
            if (cTxLock == queueUNLOCKED)
            {
                {
                    /* 如果有任务在等待获取此消息队列 */
                    if (listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
                    {
                        /* 将任务从阻塞中恢复 */
                        if (xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
                        {
                            if (pxHigherPriorityTaskWoken != NULL)
                            {
                                /* 解除阻塞的任务优先级比当前任务高,记录上下文切换请求,
                                 * 等返回中断服务程序后,就进行上下文切换 */
                                *pxHigherPriorityTaskWoken = pdTRUE;
                            }
                            else
                            {
                                mtCOVERAGE_TEST_MARKER();
                            }
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
            }
            else
            {
                /* 队列上锁,记录上锁次数,等到任务解除队列锁时,
                 * 使用这个计录数就可以知道有多少数据入队 */
                pxQueue->cTxLock = (int8_t)(cTxLock + 1);
            }

            xReturn = pdPASS;
        }
        else
        {
            /* 队列是满的,因为 API 执行的上下文环境是中断,
             * 所以不能阻塞,直接返回队列已满错误代码 errQUEUE_FULL */
            traceQUEUE_SEND_FROM_ISR_FAILED(pxQueue);
            xReturn = errQUEUE_FULL;
        }
    }
    portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);

    return xReturn;
}

1.如果队列未满或允许覆盖,完成拷贝。(锁不影响拷贝信息)

2.如果队列未上锁,检查是否有任务在等待队列中的数据。如果有,并且可以从等待列表中移除至少一个任务,则通过设置xHigherPriorityTaskWokenpdTRUE来标记需要在ISR完成后进行任务切换。

3.如果队列上锁,则增加1次锁计数器以延迟唤醒逻辑,直到队列解锁

4.如果队列已满,因为 API 执行的上下文环境是中断,不能阻塞,立刻返回错误代码 errQUEUE_FULL。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值