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
posted @ 2025-07-08 21:30  gramming  阅读(321)  评论(0)    收藏  举报