FreeRTOS(互斥信号量)

 资料来源于硬件家园:资料汇总 - FreeRTOS实时操作系统课程(多任务管理)

目录

一、互斥信号量的定义与应用

1、互斥信号量的定义

2、互斥信号量的应用

3、简要了解递归互斥信号量

二、优先级翻转问题

1、运行条件

2、优先级翻转编程测试

三、互斥信号量的运作机制

四、互斥信号量常用的API函数

 1、互斥信号量的典型流程与API

2、互斥信号量创建与删除

3、互斥信号量释放

4、互斥信号量获取

五、互斥信号量的应用编程

1、使能互斥信号量

 2、创建互斥信号量

 3、创建三个优先级不同任务验证


一、互斥信号量的定义与应用

1、互斥信号量的定义

前面学过,取值只有0与1两种状态的信号量称之为二值信号量。 而互斥信号量是一种特殊的二值信号量,具有防止优先级翻转的特性。

创建互斥信号量时,系统会为创建的互斥信号量分配内存,互斥信号量创建完成后的示意图如下:

图片

2、互斥信号量的应用

在嵌入式操作系统中,互斥信号量用于临界资源的独占式访问,只能用于任务与任务间,因为其特有的优先级继承机制只能在任务中起作用,在中断的上下文环境毫无意义。

应用场景:

比如有两个任务需要通过同一串口发送数据,其硬件资源只有一个,那么两个任务不能同时发送,否则会导致数据错误。此时就可以用互斥信号量对串口资源进行保护,当任务1正在使用串口发送数据时,互斥信号量变为无效,任务2无法使用串口,任务2必须等待互斥信号量有效(任务1释放信号量),才能获得串口使用权,进而发送数据。

3、简要了解递归互斥信号量

递归互斥信号量是一种特殊的互斥信号量,支持拥有该信号量使用权的任务重复多次获取,而不会死锁。 任务成功获取几次递归互斥信号量,就要返还几次,在此之前,递归互斥信号量都处于无效状态。

二、优先级翻转问题

使用互斥信号量,可以有效的防止优先级翻转问题。

图片

1、运行条件

 创建 3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。也就是 Task1 的优先级最高。 

 任务 Task1 和 Task3 互斥访问串口打印 printf,采用二值信号实现互斥访问。

 起初 Task3 通过二值信号量正在调用 printf,被任务 Task1 抢占,开始执行任务 Task1,也就是上图的起始位置。

 任务 Task1 运行的过程需要调用函数 printf,发现任务 Task3 正在调用,任务 Task1 会被挂起,等待 Task3 释放函数 printf。 

 在调度器的作用下,任务 Task3 得到运行,Task3 运行的过程中,由于任务 Task2 就绪,抢占了 Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务 Task1 需要等待 Task2 执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务 Task1 等待低优先级任务 Task2 完成。这种情况被称之为优先级翻转问题。 

 任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,从而可以继续执行。

2、优先级翻转编程测试

三、互斥信号量的运作机制

互斥量处理不同任务对临界资源的访问时,任务要获得互斥量才能进行资源访问。一旦有任务成功获得了互斥量,则互斥量立即变为闭锁状态,此时其他任务会因为获取不到互斥量而不能访问这个资源。任务会根据用户自定义的等待时间进行等待,直到互斥量被持有的任务释放,其他任务才能获取互斥量从而得以访问该临界资源。此时互斥量再次上锁,如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源,保证了临界资源操作的安全性,具体如下图所示。

图片

①:因为互斥量具有优先级继承机制,一般选择使用互斥量对资源进行保护。当采用互斥量保护的资源被占用时,无论是什么优先级的任务,想要使用该资源都会被阻塞

②:假如正在使用该资源的任务1比阻塞中的任务2的优先级低,那么任务1的优先级将被系统临时提升到与高优先级任务2相等(任务1的优先级从L变成H),这个就是所谓的优先级继承,这样就有效地防止了优先级翻转问题,因为此时优先级介于任务1与任务2之间的任务,抢占不了CPU。

③:当任务1使用完资源之后,释放互斥量,此时任务1的优先级会从H变回原来的L

④~⑤:任务2此时可以获得互斥量,然后进行资源的访问,当任务2访问了资源时,该互斥量的状态又变为闭锁状态,其他任务无法获取互斥量。

四、互斥信号量常用的API函数

 1、互斥信号量的典型流程与API

> 创建互斥信号量  xSemaphoreCreateMutex()

> 释放互斥信号量  xSemaphoreGive()

> 获取互斥信号量  xSemaphoreTake()

> 删除互斥信号量  vSemaphoreDelete()

2、互斥信号量创建与删除

互斥信号量控制块(句柄)

如下图:二值信号量的句柄为消息队列的句柄,因为二值信号量是一种长度为1,消息大小为0的特殊消息队列

图片

图片

图片

互斥信号量创建

函数原型:SemaphoreHandle_t xSemaphoreCreateMutex( void )

函数描述:函数 xSemaphoreCreateMutex 用于创建互斥信号量。

 返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不足,无法为此互斥信号量提供所需的空间会返回 NULL。

使用这个函数要注意以下问题

1. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:#define configUSE_MUTEXES 1

说明:此函数基于消息队列函数实现:

图片

应用举例:

图片

互斥信号量删除

函数原型:void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */

函数描述:函数 vSemaphoreDelete可用于删除互斥信号量。 

3、互斥信号量释放

函数原型:xSemaphoreGive( SemaphoreHandle_t xSemaphore ); /* 信号量句柄 */

函数描述:函数 xSemaphoreGive 用于在任务代码中释放信号量。

 第 1 个参数是信号量句柄。

 返回值,如果信号量释放成功返回 pdTRUE,否则返回 pdFALSE,因为信号量的实现是基于消息队列,返回失败的主要原因是消息队列已经满了。

使用这个函数要注意以下问题:

1. 此函数是用于任务代码中调用的,不可以在中断服务程序中调用此函数。

2. 使用此函数前,一定要保证用函数 xSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或者xSemaphoreCreateCounting()创建了信号量。

3. 此函数不支持使用 xSemaphoreCreateRecursiveMutex()创建的信号量。

应用举例:

图片

4、互斥信号量获取

函数原型:xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );         函数描述:函数 xSemaphoreTake 用于在任务代码中获取信号量。 

 第 1 个参数是信号量句柄。 

 第 2 个参数是没有信号量可用时,等待信号量可用的最大等待时间,单位系统时钟节拍。 返回值,如果创建成功会获取信号量返回 pdTRUE,否则返回 pdFALSE。

使用这个函数要注意以下问题:

1. 此函数是用于任务代码中调用的,不可以在中断服务程序中调用此函数。

2. 如果消息队列为空且第 2 个参数为 0,那么此函数会立即返回。

3. 如果用户将 FreeRTOSConfig.h 文件中的宏定义 INCLUDE_vTaskSuspend 配置为 1 且第 2 个参数配置为 portMAX_DELAY,那么此函数会永久等待直到信号量可用。

应用举例:

图片

五、互斥信号量的应用编程

1、使能互斥信号量

 2、创建互斥信号量

 3、创建三个优先级不同任务验证

  if(myMutex01Handle==NULL)
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建互斥信号量成功 \r\n",16, HAL_MAX_DELAY);
  else
	  HAL_UART_Transmit(&huart2, (uint8_t*)"创建互斥信号量失败 \r\n",16, HAL_MAX_DELAY);
void StartTask03(void const * argument)
{
  /* USER CODE BEGIN StartTask03 */
	BaseType_t xResult;
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03获取互斥信号量...\r\n",32, HAL_MAX_DELAY);
	  xResult=xSemaphoreTake(myMutex01Handle,portMAX_DELAY);

	  if(xResult==pdTRUE)
	  {
		  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03 Running...\r\n",28, HAL_MAX_DELAY);
	  }
	  HAL_UART_Transmit(&huart2, (uint8_t*)"H_StartTask03 释放互斥信号量...\r\n",35, HAL_MAX_DELAY);
	  xResult=xSemaphoreGive(myMutex01Handle);
    osDelay(500);
  }
  /* USER CODE END StartTask03 */
}
void StartTask04(void const * argument)
{
  /* USER CODE BEGIN StartTask04 */
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"M_StartTask04 runing...\r\n",27, HAL_MAX_DELAY);
    osDelay(500);
  }
  /* USER CODE END StartTask04 */
}
void StartTask05(void const * argument)
{
  /* USER CODE BEGIN StartTask05 */
	BaseType_t xResult;
  /* Infinite loop */
  for(;;)
  {
	  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03获取互斥信号量...\r\n",27, HAL_MAX_DELAY);
	  xResult=xSemaphoreTake(myMutex01Handle,portMAX_DELAY);

	  if(xResult==pdTRUE)
	  {
		  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03 Running...\r\n",28, HAL_MAX_DELAY);
	  }

	  HAL_Delay(3000);
	  HAL_UART_Transmit(&huart2, (uint8_t*)"L_StartTask03 释放互斥信号量...\r\n",28, HAL_MAX_DELAY);
	  xResult=xSemaphoreGive(myMutex01Handle);
    osDelay(500);
  }
  /* USER CODE END StartTask05 */
}

猜你喜欢

转载自blog.csdn.net/qq_57594025/article/details/132261925