说明

rtos内核基本上除了任务之间的调度以外,就剩下任务通信了。如果再广一点区分,任务通信也算任务调度了,它其实是为了提高内核效率而衍生出来的内核组件,为了将这些更规范,故而大家取名为信号量,邮箱,互斥量,任务通知等等,它们并不神秘。

代码分析

这节主要讲消息队列机制的代码分析。

首先是队列的创建,调用函数xQueueGenericCreate():

QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ){Queue_t *pxNewQueue;size_t xQueueSizeInBytes;uint8_t *pucQueueStorage;configASSERT( uxQueueLength > ( UBaseType_t ) 0 );/* Allocate enough space to hold the maximum number of items thatcan be in the queue at any time.  It is valid for uxItemSize to bezero in the case the queue is used as a semaphore. */xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. *//* Allocate the queue and storage area.  Justification for MISRAdeviation as follows:  pvPortMalloc() always ensures returned memoryblocks are aligned per the requirements of the MCU stack.  In this casepvPortMalloc() must return a pointer that is guaranteed to meet thealignment requirements of the Queue_t structure - which in this caseis an int8_t *.  Therefore, whenever the stack alignment requirementsare greater than or equal to the pointer to char requirements the castis safe.  In other cases alignment requirements are not strict (one ortwo bytes). */pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); /*lint !e9087 !e9079 see comment above. */if( pxNewQueue != NULL ){/* Jump past the queue structure to find the location of the queuestorage area. */pucQueueStorage = ( uint8_t * ) pxNewQueue;pucQueueStorage += sizeof( Queue_t ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */#if( configSUPPORT_STATIC_ALLOCATION == 1 ){/* Queues can be created either statically or dynamically, sonote this task was created dynamically in case it is laterdeleted. */pxNewQueue->ucStaticallyAllocated = pdFALSE;}#endif /* configSUPPORT_STATIC_ALLOCATION */prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );}else{traceQUEUE_CREATE_FAILED( ucQueueType );mtCOVERAGE_TEST_MARKER();}return pxNewQueue;}

首先是分配内存,然后是定位到消息队列头于消息体。后面调用prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue );

做基本的初始化,代码如下:

static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ){/* Remove compiler warnings about unused parameters shouldconfigUSE_TRACE_FACILITY not be set to 1. */( void ) ucQueueType;if( uxItemSize == ( UBaseType_t ) 0 ){/* No RAM was allocated for the queue storage area, but PC head cannotbe set to NULL because NULL is used as a key to say the queue is used asa mutex.  Therefore just set pcHead to point to the queue as a benignvalue that is known to be within the memory map. */pxNewQueue->pcHead = ( int8_t * ) pxNewQueue;}else{/* Set the head to the start of the queue storage area. */pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage;}/* Initialise the queue members as described where the queue type isdefined. */pxNewQueue->uxLength = uxQueueLength;pxNewQueue->uxItemSize = uxItemSize;( void ) xQueueGenericReset( pxNewQueue, pdTRUE );#if ( configUSE_TRACE_FACILITY == 1 ){pxNewQueue->ucQueueType = ucQueueType;}#endif /* configUSE_TRACE_FACILITY */#if( configUSE_QUEUE_SETS == 1 ){pxNewQueue->pxQueueSetContainer = NULL;}#endif /* configUSE_QUEUE_SETS */traceQUEUE_CREATE( pxNewQueue );}

最终需要调用xQueueGenericReset()做更深入的初始化,代码如下:

BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ){Queue_t * const pxQueue = xQueue;configASSERT( pxQueue );taskENTER_CRITICAL();{pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U;pxQueue->pcWriteTo = pxQueue->pcHead;pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */pxQueue->cRxLock = queueUNLOCKED;pxQueue->cTxLock = queueUNLOCKED;if( xNewQueue == pdFALSE ){/* If there are tasks blocked waiting to read from the queue, thenthe tasks will remain blocked as after this function exits the queuewill still be empty.  If there are tasks blocked waiting to write tothe queue, then one should be unblocked as after this function exitsit will be possible to write to it. */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{/* Ensure the event queues start in the correct state. */vListInitialise( &( pxQueue->xTasksWaitingToSend ) );vListInitialise( &( pxQueue->xTasksWaitingToReceive ) );}}taskEXIT_CRITICAL();/* A value is returned for calling semantic consistency with previousversions. */return pdPASS;}

最终形成这样的结构图:

说明一下,为什么pcReadFrom为什么不等于pcWriteTo。因为后面代码会执行一次pcReadFrom=pcReadFrom+1的操作。

之后就是一个任务负责发送消息,另外一个任务则负责接收任务了。

任务消息发送使用函数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 = xQueue;configASSERT( pxQueue );configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );}#endif/*lint -save -e904 This function relaxes the coding standard somewhat toallow return statements within the function itself.  This is done in theinterest of execution time efficiency. */for( ;; ){taskENTER_CRITICAL();{/* Is there room on the queue now?  The running task must be thehighest priority task wanting to access the queue.  If the head itemin the queue is to be overwritten then it does not matter if thequeue is full. */if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ){traceQUEUE_SEND( pxQueue );#if ( configUSE_QUEUE_SETS == 1 ){const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );if( pxQueue->pxQueueSetContainer != NULL ){if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ){/* Do not notify the queue set as an existing itemwas overwritten in the queue so the number of itemsin the queue has not changed. */mtCOVERAGE_TEST_MARKER();}else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ){/* The queue is a member of a queue set, and postingto the queue set caused a higher priority task tounblock. A context switch is required. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{/* If there was a task waiting for data to arrive on thequeue then unblock it now. */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* The unblocked task has a priority higher thanour own so yield immediately.  Yes it is ok todo this from within the critical section - thekernel takes care of that. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else if( xYieldRequired != pdFALSE ){/* This path is a special case that will only getexecuted if the task was holding multiple mutexesand the mutexes were given back in an order that isdifferent to that in which they were taken. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}}#else /* configUSE_QUEUE_SETS */{xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );/* If there was a task waiting for data to arrive on thequeue then unblock it now. */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* The unblocked task has a priority higher thanour own so yield immediately.  Yes it is ok to dothis from within the critical section - the kerneltakes care of that. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else if( xYieldRequired != pdFALSE ){/* This path is a special case that will only getexecuted if the task was holding multiple mutexes andthe mutexes were given back in an order that isdifferent to that in which they were taken. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_QUEUE_SETS */taskEXIT_CRITICAL();return pdPASS;}else{if( xTicksToWait == ( TickType_t ) 0 ){/* The queue was full and no block time is specified (orthe block time has expired) so leave now. */taskEXIT_CRITICAL();/* Return to the original privilege level before exitingthe function. */traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}else if( xEntryTimeSet == pdFALSE ){/* The queue was full and a block time was specified soconfigure the timeout structure. */vTaskInternalSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* Interrupts and other tasks can send to and receive from the queuenow the critical section has been exited. */vTaskSuspendAll();prvLockQueue( pxQueue );/* Update the timeout state to see if it has expired yet. */if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ){if( prvIsQueueFull( pxQueue ) != pdFALSE ){traceBLOCKING_ON_QUEUE_SEND( pxQueue );vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );/* Unlocking the queue means queue events can effect theevent list.  It is possible that interrupts occurring nowremove this task from the event list again - but as thescheduler is suspended the task will go onto the pendingready last instead of the actual ready list. */prvUnlockQueue( pxQueue );/* Resuming the scheduler will move tasks from the pendingready list into the ready list - so it is feasible that thistask is already in a ready list before it yields - in whichcase the yield will not cause a context switch unless thereis also a higher priority task in the pending ready list. */if( xTaskResumeAll() == pdFALSE ){portYIELD_WITHIN_API();}}else{/* Try again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{/* The timeout has expired. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();traceQUEUE_SEND_FAILED( pxQueue );return errQUEUE_FULL;}} /*lint -restore */}

如果任务A调用xQueueGenericSend()函数,而调用这个函数的时候,如果消息队列未满或者允许覆盖写入,则调用prvCopyDataToQueue()将数据拷贝到指定位置。当指定queueSEND_TO_BACK时,表示插入尾部,其实就是pcWriteTo的位置,否则可以插入到头部,也就是在读取的时候,直接就能读到这个拷贝过去的消息。

拷贝完数据之后,如果有任务在等待这个消息,那就需要调用函数xTaskRemoveFromEventList()将该任务从事件列表删除,并恢复等待该消息的任务,让其进入就绪态,如果它比当前任务优先级高,则需要切换过去。

假如,任务A调用xQueueGenericSend()函数而队列已满。此时,如果延时时间xTicksToWait为0,则函数需要返回。否则设置的延时时间大于0,那需要调用vTaskInternalSetTimeOutState()进行的简单初始化结构体TimeOut_t。之后是确实队列已经满了的处理,则调用vTaskPlaceOnEventList(),将当前任务的xEventListItem加入pxQueue->xTasksWaitingToSend中,并调用prvAddCurrentTaskToDelayedList()添加到延时表里面。之后调用xTaskResumeAll()恢复调度器运行。

再看看调用xQueueReceive(),函数内容为:

BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ){BaseType_t xEntryTimeSet = pdFALSE;TimeOut_t xTimeOut;Queue_t * const pxQueue = xQueue;/* Check the pointer is not NULL. */configASSERT( ( pxQueue ) );/* The buffer into which data is received can only be NULL if the data sizeis zero (so no data is copied into the buffer. */configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) );/* Cannot block if the scheduler is suspended. */#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ){configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );}#endif/*lint -save -e904  This function relaxes the coding standard somewhat toallow return statements within the function itself.  This is done in theinterest of execution time efficiency. */for( ;; ){taskENTER_CRITICAL();{const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;/* Is there data in the queue now?  To be running the calling taskmust be the highest priority task wanting to access the queue. */if( uxMessagesWaiting > ( UBaseType_t ) 0 ){/* Data available, remove one item. */prvCopyDataFromQueue( pxQueue, pvBuffer );traceQUEUE_RECEIVE( pxQueue );pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1;/* There is now space in the queue, were any tasks waiting topost to the queue?  If so, unblock the highest priority waitingtask. */if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}taskEXIT_CRITICAL();return pdPASS;}else{if( xTicksToWait == ( TickType_t ) 0 ){/* The queue was empty and no block time is specified (orthe block time has expired) so leave now. */taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else if( xEntryTimeSet == pdFALSE ){/* The queue was empty and a block time was specified soconfigure the timeout structure. */vTaskInternalSetTimeOutState( &xTimeOut );xEntryTimeSet = pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* Interrupts and other tasks can send to and receive from the queuenow the critical section has been exited. */vTaskSuspendAll();prvLockQueue( pxQueue );/* Update the timeout state to see if it has expired yet. */if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ){/* The timeout has not expired.  If the queue is still empty placethe task on the list of tasks waiting to receive from the queue. */if( prvIsQueueEmpty( pxQueue ) != pdFALSE ){traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );prvUnlockQueue( pxQueue );if( xTaskResumeAll() == pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}else{/* The queue contains data again.  Loop back to try and read thedata. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{/* Timed out.  If there is no data in the queue exit, otherwise loopback and attempt to read the data. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();if( prvIsQueueEmpty( pxQueue ) != pdFALSE ){traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else{mtCOVERAGE_TEST_MARKER();}}} /*lint -restore */}

如果队列有数据(uxMessagesWaiting>0),说明队列可读,则需要调用prvCopyDataFromQueue()拷贝数据。如果pxQueue->xTasksWaitingToSend有item,则会调用xTaskRemoveFromEventList()将pxQueue->xTasksWaitingToSend中的一项item删除,而且也会将其加入到就就绪表中。如果队列为空,则跟前面发送消息队列数据类似,则需要在pxQueue->xTasksWaitingToReceive中加入pxCurrentTCB->xEventListItem,并且添加到延时表中。

freertos 创建互斥量_freertos任务通信相关推荐

  1. freertos 创建互斥量_FreeRTOS的信号量和互斥量

    1. 理解如下,言简意赅的说,信号量解决同步,互斥量解决竞争. 信号量用于同步,主要任务间和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁. 互斥量具有优先级继承,信 ...

  2. freertos 创建互斥量_FreeRTOS互斥信号量

    FreeRTOS互斥信号量 本文完整版地址:http://http://bbs.armfly.com/read.php?tid=21381 本章节讲解FreeRTOS重要的资源共享机制---互斥信号量 ...

  3. freertos 创建互斥量_STM32CubeMX+FreeRTOS学习[6] 互斥量(Lu)

    FreeRTOS 学习之六:互斥量 前提:默认已经装好 MDK V5 和 STM32CubeMX ,并安装了 STM32F1xx 系列的支持包. 硬件平台: STM32F1xx 系列. 目的:学习互斥 ...

  4. FreeRTOS互斥量 基于STM32

    文章目录 一.互斥量基本概念 二.互斥量的优先级继承机制 三.互斥量应用场景 四.互斥量运作机制 五.互斥量函数接口讲解 1.互斥量创建函数 xSemaphoreCreateMutex() 2.递归x ...

  5. 5.FreeRTOS学习笔记- 互斥量

    基本概念 互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量 互斥量 支持互斥量所有权.递归访问以及防止优先级翻转的特性,用于实现对临界资源(如显示器.打印机)的独占式访问. 任意时刻互斥量 ...

  6. FreeRTOS个人笔记-互斥量

    根据个人的学习方向,学习FreeRTOS.由于野火小哥把FreeRTOS讲得比较含蓄,打算在本专栏尽量细化一点.作为个人笔记,仅供参考或查阅. 配套资料:FreeRTOS内核实现与应用开发实战指南.野 ...

  7. FreeRTOS的信号量和互斥量

    1. 理解如下,言简意赅的说,信号量解决同步,互斥量解决竞争. 信号量用于同步,主要任务间和中断间同步:互斥量用于互锁,用于保护同时只能有一个任务访问的资源,为资源上一把锁. 互斥量具有优先级继承,信 ...

  8. RT-Thread 线程同步及通信 -- 信号量、互斥量、事件、邮箱、消息队列

    目录 一  RT-Thread 信号量 二  RT-Thread 互斥量 三  RT-Thread 事件标志组 四  RT-Thread 邮箱 五  RT-Thread 消息队列 一  RT-Thre ...

  9. Visual C++利用互斥量同步线程实现文件读取进度条

    忘了原文的位置了. 一.前言 文件读取进度条的实现可以有很多种方法,常用的是在读取文件的过程中隔一定时间向对话框发送消息以控制进度条的位置,但是这种方法很难确定隔多少时问发送一个消息,因为文件的大小是 ...

最新文章

  1. 一个项目中能提出哪些数据库优化_如何有效进行项目集管理?
  2. 无盘服务器 双镜像盘,镜像(无盘柜)-双活集群解决方案
  3. 万年自学党聊聊如何选择编程学习资源?
  4. c++: size_type与 size_t一些概念
  5. php什么集成框架比较好,php哪个框架比较好?
  6. Linux 上配置网络设备命令举例
  7. Android Wear 发布 4 年终改名,继续艰难存活
  8. PAT 乙级 1016. 部分A+B (15) Java版
  9. 深度学习根据文字生成图片教程(附python代码)
  10. eclipse实用编辑快捷键
  11. 早停法的应用(keras)
  12. win2008服务器系统玩红警,win8系统不能玩红警2如何解决?win8系统不能玩红警2的解决方法...
  13. uniapp 定位服务_uniapp使用高德地图定位
  14. 微信开发者工具命令面版
  15. 搜狗workflow项目研究(三)线程池
  16. php面试英文自我介绍范文带翻译,英文自我介绍范文3篇
  17. 找不到驱动器。名为“.C”的驱动器不存在。的问题总结
  18. MODIS数据批量裁剪并合成月尺度数据:以MOD13A1为例
  19. 佛系三连 都行 都可以 没关系
  20. python 乘法口诀表

热门文章

  1. 漫画:一位文科生的编程之路。
  2. Android API Guides---Bluetooth
  3. Asp.Net 控件生命周期
  4. 基于c++的音乐播放器(1)
  5. 一步一步学WF系列(四)——工作流模拟登陆
  6. 杀毒软件“驱逐舰”序列号、组件和病毒库升级下载地址
  7. php 实现树状组织图插件,使用jstree插件实现树形结构
  8. 服役七年超级计算机,太费电 曾经的最快超级计算机服役5年即退休
  9. 获取系统分辨率_一文弄懂高分辨率高速快门CMOS成像传感器技术应用现状
  10. java压缩传输gzip_服务器使用Gzip压缩数据,加快网络传输(Java 例子)