FreeRTOS的信号量和互斥量源码分析
一、信号量和互斥量介绍
FreeRTOS的信号量和互斥量是限制资源访问数量的机制,传递状态,实现任务间的同步。信号量有计数信号量和二值信号量,互斥量相较于二值信号量能够解决优先级反转的问题。
二、信号量创建函数
// 创建计数型信号量
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle;
xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); // 队列元素内容长度=信号量计数值、大小是0
// item长度为0,只分配了sizeof( Queue_t )大小的空间,其余与消息队列创建无明显区别
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; // 初始元素个数不为0
return xHandle;
}
// 创建二值信号量
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
三、信号量删除函数
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
四、信号量give和take函数
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
// give函数实际通过队列的send函数实现,待发送的元素是NULL,等待时间为0,因此
// prvCopyDataToQueue函数,因为元素的大小是0,因此不产生memcpy行为
// 同时不存在超时等待,不会阻塞
#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 = ( Queue_t * ) xQueue;// 队列头
for( ;; )
{
taskENTER_CRITICAL(); // 关中断
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; // 信号量计数值
if( uxSemaphoreCount > ( UBaseType_t ) 0 ) // 有资源
{
pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1; // 信号量计数值减1
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) // 等待send的队列中有task
{
xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ); // 唤醒
// 调度
}
taskEXIT_CRITICAL(); // 开中断
return pdPASS; // 返回成功
}
else // 没有资源
{
// 如果不等待,则开中断并返回errQUEUE_EMPTY
// 如果等待
vTaskInternalSetTimeOutState( &xTimeOut ); // 构建超时结构体
xEntryTimeSet = pdTRUE; // 设置超时的标志
}
taskEXIT_CRITICAL(); // 开中断
vTaskSuspendAll(); // 暂停所有任务
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) // 检查超时时间,当前tick会增加,要等待的tick会减少,pdFALSE表示时间还未到
{
if( prvIsQueueFull( pxQueue ) != pdFALSE ) // 队列满
{
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); // 将当前任务块挂入等待队列
( void ) xTaskResumeAll();
}
else
{
( void ) xTaskResumeAll();
}
}
else // 超时时间到
{
( void ) xTaskResumeAll(); // 调度
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
return errQUEUE_EMPTY; // 返回错误
}
}
}
}
五、互斥量的创建函数
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; // 二值的信号量
pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); // 和创建信号量相同
prvInitialiseMutex( pxNewQueue );
// 设置pxMutexHolder为NULL
// 设置uxRecursiveCallCount为0
// 调用xQueueGenericSend
// prvCopyDataToQueue时,调用xTaskPriorityDisinherit,do-nothing,如果pxMutexHolder非空,则将优先级调整为原始的优先级
return pxNewQueue;
}
六、互斥量的删除、give、take函数
使用semaphore函数的API
take函数,获取互斥量的时候设置pxMutexHolder成员,同时在检查超时时,使用xTaskPriorityInherit设置优先级继承,提高mutex的拥有者的优先级为当前任务的优先级(如果当前任务优先级比较高)
即由于低优先级任务获得mutex导致高优先级任务获取mutex时获取不到,让低优先级任务继承高优先级任务的优先级,尽快执行完释放mutex,调用give函数释放mutex时,在prvCopyDataToQueue函数中,调整任务的优先级为原优先级,即uxBasePriority

浙公网安备 33010602011771号