基本概念
- 互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量
- 互斥量 支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源(如显示器、打印机)的独占式访问。
- 任意时刻互斥量的状态只有两种,开锁或闭锁。
- 持有该互斥量的任务也能够重复获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性,信号量重复获取则会被挂起
- 互斥量与二值信号量最大的不同是:互斥量具有优先级继承机制,而信号量没有
优先级继承机制
- 优先级继承算法是指,暂时提高某个占有某种资源的低优先级任务的优先级,使之和所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值
- 优先级提升的过程叫做优先级继承
优先级翻转
高优先级任务无法运行而低优先级任务可以运行的现象称为“优先级翻转”
互斥量应用场景
- 可能会引起优先级翻转的情况
- 任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题
- 多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问
- 例如串口资源的保护
函数
头文件
#include <semphr.h>
互斥量句柄
和消息队列一样
QueueHandle_t
控制块
互斥量控制块结构体与消息队列结构体是一模一样的,只不过结构体中某些成员变量代表的含义不一样而已
typedef struct QueueDefinition
{
int8_t *pcHead;
int8_t *pcTail;
int8_t *pcWriteTo;
union
{
int8_t *pcReadFrom;
UBaseType_t uxRecursiveCallCount; //当结构体用于互斥量时,uxRecursiveCallCount 用于计数,记录递归互斥量被“调用”的次数
} u;
List_t xTasksWaitingToSend;
List_t xTasksWaitingToReceive;
volatile UBaseType_t uxMessagesWaiting;/*
这个值就表示有效互斥量个数,这个值是 1则表示互斥量有效,如果是 0 则表示互斥量无效*/
UBaseType_t uxLength; /*
如果控制块结构体被用于互斥量的时候,uxLength 表示最大的信号量可用个数,uxLength 最大为 1,因为信号量要么是有效的,要么是无效的*/
UBaseType_t uxItemSize; /*如果控制块结构体被用于互斥量的时候,则无需存储空间,为 0 即可。 */
volatile int8_t cRxLock;
volatile int8_t cTxLock;
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
函数接口
创建互斥量xSemaphoreCreateMutex()
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
递归互斥量创建函数 xSemaphoreCreateRecursiveMutex()
#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
互斥量删除函数 vSemaphoreDelete()
和信号量一样的函数
互斥量获取函数 xSemaphoreTake()
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )
/*
xSemaphore 信号量句柄。
xBlockTime 等待信号量可用的最大超时时间,单位为 tick(即系统节拍周期)。如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参xTicksToWait 设置为 portMAX_DELAY ,则任务将一直阻塞在该信号量上(即没有超时时间)。
返回值 获 取 成 功 则 返 回 pdTRUE , 获取失败 返 回 errQUEUE_EMPTY。
*/
递归互斥量获取函数 xSemaphoreTakeRecursive()
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
/*
xMutex 信号量句柄。
xBlockTime 如果不是持有互斥量的任务去获取无效的互斥量,那么任务将进行等待用户指定 超时时间,单位 为 tick(即系统节拍周期) 。如果宏 INCLUDE_vTaskSuspend 定义为 1 且形参 xTicksToWait 设置为portMAX_DELAY ,则任务将一直阻塞在该递归互斥量上(即没有超时时间)。
返回值 获取成功则返回 pdTRUE,在超时之前没有获取成功则返回 errQUEUE_EMPTY。*/
互斥量释放函数 xSemaphoreGive()
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore )
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
/*
如果信号量满,则返回错误代码(err_QUEUE_FULL)
*/
递归互斥量释放函数 xSemaphoreGiveRecursive()
#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )
示例:
之前2个任务的优先级一样,导致就是发送信号任务要打印,接收信号端不打印,引入锁后问题解决
#include "FreeRTOS.h"
#include "task.h"
#include<bsp_key.h>
#include "semphr.h"
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static TaskHandle_t send_Task_Handle = NULL;
static TaskHandle_t recv_Task_Handle = NULL;
static SemaphoreHandle_t xsem=NULL;
static SemaphoreHandle_t xlock=NULL;
static void recv_Task(void * param)
{
uint32_t data1;
while (1)
{
if(pdTRUE==xSemaphoreTake(xsem,portMAX_DELAY))
{
xSemaphoreTake(xlock,portMAX_DELAY );
printf("take sem\r\n");
xSemaphoreGive(xlock);
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
}
}
}
static void send_Task(void * param)
{
uint32_t data1=10;
while (1)
{
if(Key_Scan(KEY1_GPIO_Port,KEY1_Pin)==KEY_ON){
xSemaphoreGive(xsem);
xSemaphoreTake(xlock,portMAX_DELAY );
printf("give sem\r\n");
xSemaphoreGive(xlock);
}
vTaskDelay(100);
}
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("hello FreeRTOS\r\n");
//xqueue = xQueueCreate(10, 4);
xsem = xSemaphoreCreateBinary();
xlock = xSemaphoreCreateMutex();
if(xsem==NULL)
{
printf("create queue fail\r\n");
}
BaseType_t xReturn;
xReturn = xTaskCreate((TaskFunction_t) recv_Task, /* 任务入口函数 */
(const char *) "recv_Task", /* 任务名字 */
(uint16_t) 512, /* 任务栈大小 */
(void *) NULL, /* 任务入口函数参数 */
(UBaseType_t) 2, /* 任务的优先级 */
(TaskHandle_t *) &recv_Task_Handle); /* 任务控制块指针 */
xReturn = xTaskCreate((TaskFunction_t) send_Task, /* 任务入口函数 */
(const char *) "send_Task", /* 任务名字 */
(uint16_t) 512, /* 任务栈大小 */
(void *) NULL, /* 任务入口函数参数 */
(UBaseType_t) 2, /* 任务的优先级 */
(TaskHandle_t *) &send_Task_Handle); /* 任务控制块指针 */
if (pdPASS == xReturn)
vTaskStartScheduler(); /* 启动任务,开启调度 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
HAL_Delay(50);
}
/* USER CODE END 3 */
}