文化传播公司网站建设需求,wordpress supports,公司建设网站请示,低代码平台前面介绍过#xff0c;队列#xff08;queue#xff09;可以用于传输数据#xff1a;在任务之间#xff0c;任务和中断之间。消息队列用于传输多个数据#xff0c;但是有时候我们只需要传递一个状态#xff0c;这个状态值需要用一个数值表示#xff0c;比如#xff1a…前面介绍过队列queue可以用于传输数据在任务之间任务和中断之间。消息队列用于传输多个数据但是有时候我们只需要传递一个状态这个状态值需要用一个数值表示比如卖家做好了1个包子做好了2个包子做好了3个包子买家买了一个包子包子数量减1这个停车位我占了停车位减1我开车走了停车位加1在这些情况下我们只需要维护一个数值使用信号量效率更高更节省内存。FreeRTOS信号量简介 信号量是一种解决同步问题的机制可以实现对共享资源的有序访问。其中“同步”指的是任务之间的同步即信号量可以使得一个任务等待另一个任务完成某件事后才继续执行而“有序访问”指的是对被多任务或中断访问的共享资源如全局变量的管理当一个任务在访问读取或写入一个共享资源时信号量可以防止其他任务或中断在这期间访问读取或写入这个共享资源。FreeRTOS二值信号量前面说过信号量是基于队列实现的二值信号量也不例外二值信号量实际上就是一个队列长度为1的队列在这种情况下队列就只有空和满两种情况这就是二值信号量二值信号量通常用于互斥访问或任务同步与互斥信号量类似但是二值信号量有优先级反转的问题优先级反转就是指当一个高优先级任务因获取一个低优先级任务获取而处于没有资源状态的二值信号量时这个高优先级的任务就将被阻塞直到低优先级任务释放二值信号量在此之前如果有一个优先级介于高优先级和低优先级之间的中等优先级任务就绪那么这个中等优先级任务就会抢占低优先级任务那么这三个任务中高优先级任务反而要最后执行这就是二值信号量带来的优先级反转问题。FreeRTOS计数型信号量计数型信号量和二值信号量类似二值信号量相当于队列长度为1 的队列因此二值信号量只能容纳一个资源这也是为什么命名为二值信号量而计数型信号量相当于队列长度大于0的队列因此计数型信号零能容纳多个资源这是在计数型信号量被创建的时候确定的。计数型信号量适用于下面两种场合:事件计数在这种场合下每次事件发生后在事件处理函数中释放计数型信号量计数型信号量的资源数加1其他等待事件发生的任务 获取计数型信号量计数型信号量的资源数减1这么以来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作在这种场合下计数型信号量的资源数一般在创建时设置为0。资源管理在这种场合下计数型信号量的资源数代表着共享资源的可用数量例如前面举例中的停车场中的空车位。一个任务想要访问共享资源就必须先获取这个共享资源的计数型信号量之后在成功获取了计数型信号量之后才可以对这个共享资源进行操作当然在使用完共享资源之后也要释放这个共享资源的计数型信号量。在这种场合下计数型信号量的资源数一般在创建时设置为受其管理的共享资源的最大可用数量。信号量的特性信号量的常规操作信号量这个名字起的很恰当信号起通知作用量还可以表示资源的数量当“量”没有限制时它就是“计数型信号量”Counting Semaphores当“量”只有0、1两个取值时它就是“二值信号量”Binary Seamphores支持的动作“give”给出资源计数值加1“take”获得资源计数值减1计数信号量的典型场景是事件计数事件产生时“give信号量让计数值加1处理事件时要先”take“信号量也就是获得信号量让计数值减1资源管理要想访问资源需要先”take“信号量让计数值减1用完资源后”give“信号量让计数值加1信号量的”give“、”take“双方并不需要相同可以用于生产者-消费者场合生产者为任务AB,消费者为任务C,D一开始信号量的计数值为0如果任务CD想获得信号量会有两种结果阻塞买不到东西咱就等等吧可以定个闹钟超时时间即可返回失败不等任务AB可以生产资源就是让信号量的计数值增加1并且把等待这个资源的顾客唤醒唤醒谁谁优先级高就唤醒谁如果大家优先级都一样就唤醒等待时间最长的人二值信号量和计数型信号量的唯一差别就是计数值的最大值被限定为1信号量跟队列的对比队列信号量可以容纳多个数据创建队列时有2部分内存队列结构体、存储数据的空间只有计数值无法容纳其他数据创建信号量时只需要分配信号量结构体生产者没有空间存入数据时可以阻塞生产者用于不阻塞计数值已经达到最大值时返回失败消费者没有数据时可以阻塞消费者没有资源时可以阻塞两种信号量对比二进制信号量计数型信号量被创建时初始值为0被创建时初始值可以设定其他操作是一样的其他操作是一样的二值信号量二值信号量的本质是一个队列长度为1的队列该队列就只有空和满两种情况这就是二值。二值信号量通常用于互斥访问或任务同步与互斥信号量比较类似但是二值信号量有可能会导致优先级翻转的问题所以二值信号量更适合用于同步。二值信号量相关API函数使用二值信号量的过程创建二值信号量-释放二值信号量-获取二值信号量使用信号量时先创建然后去添加资源、获得资源使用句柄来表示一个信号量函数描述xSemaphoreCreateBinary()使用动态方式创建二值信号量xSemaphoreCreateBinaryStatic()使用静态方式创建二值信号量xSemaphoreGive()释放二值信号量xSemaphoreGiveFromISR()在中断中释放信号量xSemaphoreTake()获取信号量xSemaphoreTakeFromISR()在中断中获取信号量这些函数其实都可以在semphr.h头文件中找到它们都是一些宏定义本质上用的其实都是Queue的函数创建二值信号量/*
创建一个二进制信号量返回它的句柄
此函数内部会分配信号量结构体
返回值返回句柄非NULL表示成功可以看出动态创建二值信号量实际上是调用了函数xQueueGenericCreate()
创建了一个队列长度为1且队列项目大小为信号量队列项目大小的二值信号量类型队列
*/
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )/*
创建一个二进制信号量返回它的句柄
此函数无需动态分配内存所以需要现有一个StaticSemaphore_t结构体并传入它的指针
返回值返回句柄非NULL表示成功从 上 面 的 代 码 中 可 以 看 出 函 数 xSemaphoreCreateStatic() 实 际 上 是 调 用 了 函 数
xQueueGenericCreateStatic()创建了一个队列长度为 1 且队列项目大小为信号量队列项目大小的
二值信号量类型队列需要用户手动分配提供创建二值信号量的内存
*/
#define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticSemaphore, queueQUEUE_TYPE_BINARY_SEMAPHORE )获取信号量/*
此函数用于获取信号量如果信号量处于没有资源的状态那么此函数可以选择将任务进
行阻塞如果成功获取了信号量那信号量的资源数将会减 1。该函数实际上是一个宏定义
*/
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,TickType_t xTicksToWait )
{BaseType_t xEntryTimeSet pdFALSE;TimeOut_t xTimeOut;Queue_t * const pxQueue xQueue;#if ( configUSE_MUTEXES 1 )BaseType_t xInheritanceOccurred pdFALSE;#endif/* Check the queue pointer is not NULL. */configASSERT( ( pxQueue ) );/* 信号量类型队列的项目大小为0 */configASSERT( pxQueue-uxItemSize 0 );/* 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 to allow return* statements within the function itself. This is done in the interest* of execution time efficiency. */for( ; ; ){ /*进入临界区*/taskENTER_CRITICAL();{/* 获取信号量的资源数 */const UBaseType_t uxSemaphoreCount pxQueue-uxMessagesWaiting;/* 判断信号量是否有资源 */if( uxSemaphoreCount ( UBaseType_t ) 0 ){traceQUEUE_RECEIVE( pxQueue );/* 更新信号量的资源数 */pxQueue-uxMessagesWaiting uxSemaphoreCount - ( UBaseType_t ) 1;#if ( configUSE_MUTEXES 1 ){if( pxQueue-uxQueueType queueQUEUE_IS_MUTEX ){/* 设置互斥信号量的持有者并更新互斥信号量的持有次数 */pxQueue-u.xSemaphore.xMutexHolder pvTaskIncrementMutexHeldCount();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES *//* 判断信号量的获取阻塞任务列表中是否有任务 */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 ){/* 此宏用于启用互斥信号量 */#if ( configUSE_MUTEXES 1 ){configASSERT( xInheritanceOccurred pdFALSE );}#endif /* configUSE_MUTEXES *//* 退出临界区*/taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}/*选择阻塞等待信号量*/else if( xEntryTimeSet pdFALSE ){/* 队列满任务需要阻塞记录此时系统节拍计数器 值和溢出次数用于下面对阻塞时间进行补偿*/vTaskInternalSetTimeOutState( xTimeOut );xEntryTimeSet pdTRUE;}else{/* Entry time was already set. */mtCOVERAGE_TEST_MARKER();}}}taskEXIT_CRITICAL();/* 挂起任务调度器 */vTaskSuspendAll();/*信号量队列上锁*/prvLockQueue( pxQueue );/* 判断阻塞时间补偿后是否还需要阻塞 */if( xTaskCheckForTimeOut( xTimeOut, xTicksToWait ) pdFALSE ){/* 判断队列是否为空 */if( prvIsQueueEmpty( pxQueue ) ! pdFALSE ){traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );#if ( configUSE_MUTEXES 1 ){if( pxQueue-uxQueueType queueQUEUE_IS_MUTEX ){taskENTER_CRITICAL();{/*进行优先级继承这是互斥信号量用于解决优先级翻转问题的*/xInheritanceOccurred xTaskPriorityInherit( pxQueue-u.xSemaphore.xMutexHolder );}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* if ( configUSE_MUTEXES 1 ) *//*将任务添加到队列写入阻塞任务列表中进行阻塞*/vTaskPlaceOnEventList( ( pxQueue-xTasksWaitingToReceive ), xTicksToWait );prvUnlockQueue( pxQueue );/*恢复任务调度器*/if( xTaskResumeAll() pdFALSE ){portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}/*队列不为空*/else{/* There was no timeout and the semaphore count was not 0, so* attempt to take the semaphore again. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();}}else{/* Timed out. */prvUnlockQueue( pxQueue );( void ) xTaskResumeAll();/* If the semaphore count is 0 exit now as the timeout has* expired. Otherwise return to attempt to take the semaphore that is* known to be available. As semaphores are implemented by queues the* queue being empty is equivalent to the semaphore count being 0. */if( prvIsQueueEmpty( pxQueue ) ! pdFALSE ){#if ( configUSE_MUTEXES 1 ){/* xInheritanceOccurred could only have be set if* pxQueue-uxQueueType queueQUEUE_IS_MUTEX so no need to* test the mutex type again to check it is actually a mutex. */if( xInheritanceOccurred ! pdFALSE ){taskENTER_CRITICAL();{UBaseType_t uxHighestWaitingPriority;/* This task blocking on the mutex caused another* task to inherit this tasks priority. Now this task* has timed out the priority should be disinherited* again, but only as low as the next highest priority* task that is waiting for the same mutex. */uxHighestWaitingPriority prvGetDisinheritPriorityAfterTimeout( pxQueue );vTaskPriorityDisinheritAfterTimeout( pxQueue-u.xSemaphore.xMutexHolder, uxHighestWaitingPriority );}taskEXIT_CRITICAL();}}#endif /* configUSE_MUTEXES */traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else{mtCOVERAGE_TEST_MARKER();}}} /*lint -restore */
}释放信号量#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )这里其实和上面的获取是类似的碍于篇幅有限不给出源码。在任务中使用在ISR中使用givexSemaphoreGivexSemaphoreGiveFromISRtake xSemaphoreTakexSemaphoreTakeFromISR删除对于动态创建的信号量不再需要使用它们时可以删除它们以回收内存vSemaphoreDelete可以删除二值信号量计数型信号量函数原型如下#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )计数型信号量计数型信号量相当于队列长度大于1的队列因此计数型信号量能够容纳多个资源这在计数型信号量被创建的时候确定。计数型信号量相关API函数函数描述xSemaphoreCreateCounting()使用动态方法创建计数型信号量xSemaphoreCreateCountingStatic()使用静态方法创建计数型信号量uxSemaphoreGetCount()获取信号量的计数值计数值信号量的获取和释放和二值信号量的相同这里不再赘述。计数型信号量创建/*
uxMaxCount:计数值的最大值限定
uxInitialCount:计数值的初始值
NULL:创建失败
其他值创建成功返回计数型信号量的句柄
*/
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )获取信号量当前计数值大小/*
xSemaphore信号量句柄
返回值整数当前信号量的计数值大小
*/
#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
个人总结信号量是一种特殊的队列其中二值信号量是长度为1的队列计数型信号量是长度大于1的队列使用信号量只是传递一个状态而不是像队列一样可以缓存数据然后进行数据交流信号量相比于队列更加轻量化只需要创建信号量结构体队列不仅需要申请创建队列的结构体还包括存储数据的缓存区。二值信号量其实是一种特殊的计数型信号量只有0和1两个计数值常常用于互斥访问和任务间同步但是二值信号量会导致优先级翻转的问题互斥量被引申出解决该问题所以二值信号量最合适用于任务同步。其实不管是信号量队列互斥量事件组等IPC(在线程通信)都是这样都要先创建一个中间结构体任务之间通过该结构体传输信息进行交流这是一种间接的任务之间通信方式。