【freertos互斥量补充】递归锁
一、什么是递归(recursive)锁
递归锁(Recursive Mutex)是互斥量的 “增强版”,允许同一任务多次获取同一把锁,且不会因重复申请导致死锁。解锁时需 “配对释放”(获取多少次,就要释放多少次)。
-
普通互斥量(Mutex):
同一任务若已持有锁,再次调用xSemaphoreTake()
会立即阻塞(自己等自己,导致死锁)。 -
递归锁(Recursive Mutex):
同一任务可多次获取锁(内部通过计数器记录获取次数),每次获取只需对应释放,避免死锁。
二、怎么使用
以下是使用方法,对比参考
特性 | 普通互斥量(Mutex) | 递归锁(Recursive Mutex) |
---|---|---|
重复获取 | 同一任务重复获取 → 死锁(自己等自己) | 同一任务可重复获取(内部计数 + 1) |
释放规则 | 调用 xSemaphoreGive() 直接释放 | 需调用 xSemaphoreGiveRecursive() ,且获取次数需与释放次数配对 |
适用场景 | 简单临界区(单层访问) | 嵌套临界区(多层函数调用需同一把锁) |
创建 API | xSemaphoreCreateMutex() | xSemaphoreCreateRecursiveMutex() |
获取 API | xSemaphoreTake() | xSemaphoreTakeRecursive() |
释放 API | xSemaphoreGive() | xSemaphoreGiveRecursive() |
三、示例代码
/*
递归锁可以保证不同任务或者甚至是不同函数内创建的互斥量无法互相被获取或者释放(见Task5)。*//* Standard includes. */
#include <stdio.h>/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"/* Library includes. */
#include "stm32f10x_it.h"/* Demo app includes. */
#include "serial.h"/*-----------------------------------------------------------*//** Configure the clocks, GPIO and other peripherals as required by the demo.*/
static void prvSetupHardware( void );/** Retargets the C library printf function to the USART.*/
int fputc( int ch, FILE *f );/** Configures the timers and interrupts for the fast interrupt test as* described at the top of this file.*/
extern void vSetupTimerTest( void );/*-----------------------------------------------------------*/static int sum = 0;
static volatile int flagCalcEnd = 0;
static volatile int flagUARTused = 0;static SemaphoreHandle_t xSemCalc;
static SemaphoreHandle_t xSemUART;void Task1Function(void * param)
{volatile int i = 0;while (1){for (i = 0; i < 10000000; i++)sum++;//printf("1");xSemaphoreGive(xSemCalc);vTaskDelete(NULL);}
}void Task2Function(void * param)
{while (1){//if (flagCalcEnd)flagCalcEnd = 0;xSemaphoreTake(xSemCalc, portMAX_DELAY);flagCalcEnd = 1;printf("sum = %d\r\n", sum);}
}void TaskGenericFunction(void * param)
{int i;while (1){xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);printf("%s\r\n", (char *)param);for (i = 0; i < 10; i++){xSemaphoreTakeRecursive(xSemUART, portMAX_DELAY);printf("%s in loop %d\r\n", (char *)param, i);xSemaphoreGiveRecursive(xSemUART);}xSemaphoreGiveRecursive(xSemUART);vTaskDelay(1);}
}void Task5Function(void * param)
{vTaskDelay(10);while (1){while (1){if (xSemaphoreTakeRecursive(xSemUART, 0) != pdTRUE){xSemaphoreGiveRecursive(xSemUART); }else{break;}}printf("%s\r\n", (char *)param);xSemaphoreGiveRecursive(xSemUART);vTaskDelay(1);}
}/*-----------------------------------------------------------*/int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf("Hello, world!\r\n");xSemCalc = xSemaphoreCreateCounting(10, 0);//xSemUART = xSemaphoreCreateBinary();//xSemaphoreGive(xSemUART);//xSemUART = xSemaphoreCreateMutex();xSemUART = xSemaphoreCreateRecursiveMutex();xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);xTaskCreate(TaskGenericFunction, "Task3", 100, "Task 3 is running", 1, NULL);xTaskCreate(TaskGenericFunction, "Task4", 100, "Task 4 is running", 1, NULL);xTaskCreate(Task5Function, "Task5", 100, "Task 5 is running", 1, NULL);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}
/*-----------------------------------------------------------*//*-----------------------------------------------------------*//*-----------------------------------------------------------*/static void prvSetupHardware( void )
{/* Start with the clocks in their expected state. */RCC_DeInit();/* Enable HSE (high speed external clock). */RCC_HSEConfig( RCC_HSE_ON );/* Wait till HSE is ready. */while( RCC_GetFlagStatus( RCC_FLAG_HSERDY ) == RESET ){}/* 2 wait states required on the flash. */*( ( unsigned long * ) 0x40022000 ) = 0x02;/* HCLK = SYSCLK */RCC_HCLKConfig( RCC_SYSCLK_Div1 );/* PCLK2 = HCLK */RCC_PCLK2Config( RCC_HCLK_Div1 );/* PCLK1 = HCLK/2 */RCC_PCLK1Config( RCC_HCLK_Div2 );/* PLLCLK = 8MHz * 9 = 72 MHz. */RCC_PLLConfig( RCC_PLLSource_HSE_Div1, RCC_PLLMul_9 );/* Enable PLL. */RCC_PLLCmd( ENABLE );/* Wait till PLL is ready. */while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}/* Select PLL as system clock source. */RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK );/* Wait till PLL is used as system clock source. */while( RCC_GetSYSCLKSource() != 0x08 ){}/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC| RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE );/* SPI2 Periph clock enable */RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );/* Set the Vector Table base address at 0x08000000 */NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );/* Configure HCLK clock as SysTick clock source. */SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );SerialPortInit();
}
/*-----------------------------------------------------------*//*-----------------------------------------------------------*//*-----------------------------------------------------------*/#ifdef DEBUG
/* Keep the linker happy. */
void assert_failed( unsigned char* pcFile, unsigned long ulLine )
{for( ;; ){}
}
#endif