一起使用二进制信号量和互斥量

Posted

技术标签:

【中文标题】一起使用二进制信号量和互斥量【英文标题】:Using Binary Semaphore and Mutex Together 【发布时间】:2021-08-14 06:15:50 【问题描述】:

我是 FreeRTOS 新手,一直在阅读 FreeRTOS 文档并在 STM32F767 Nucleo 板上使用 FreeRTOS 编写简单代码。在我编写的简单程序中,我使用二进制信号量仅在通过xSemaphoreGiveFromISR() 发生 LPTIM 和 GPIO 中断时向某些任务发出信号,并通过xSemaphoreGive() 向另一个任务发出信号以执行来自另一个任务的某些操作。

假设我有一个 I2C1 外设连接到两个不同的设备:

一个加速度计,只要发生活动/移动,就会触发微控制器的 GPIO 中断。此 GPIO 中断向微控制器发出信号,表明必须读取其中断事件寄存器中的一段数据,以便可以再次发出下一个活动/移动事件的信号。 必须定期读取的设备,将通过 LPTIM 或 TIM 外围设备触发

我可以在上述情况下使用互斥量和二进制信号量吗?

二进制信号量将向任务指示需要根据触发的相应中断执行操作,但互斥锁将在这两个任务之间共享,其中 Task1 将负责从加速度计读取数据, Task2 将负责从其他设备读取数据。我在想将使用互斥锁,因为这两个操作永远不应该一起发生,这样就不会在总线上发生可能锁定任一 I2C 设备的重叠 I2C 事务。

代码如下所示:

void Task1_AccelerometerOperations(void *argument)

   /* The Semaphore will be given from the GPIO Interrupt Handler, signalling that a piece of 
      data needs to be read from the accelerometer through I2C. */
   if(xSemaphoreTake(xSemaphore_GPIOInterruptFlag, portMAX_DELAY) == pdTRUE)
   
      /* This Mutex should ensure that only one I2C transaction can happen at a time */
      if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
      
         /* Perform I2C Transaction */
         /* Perform operations with the data received */

         /* Mutex will be given back, indicating that the shared I2C Bus is now available */
         xSemaphoreGiveRecursive(xMutex_I2CBus);
      
      else
      
         /* Mutex was not available even after 2 seconds since the GPIO interrupt triggered. 
            Perform Error Handling for the event that the I2C bus was locked */
      

      /* Piece of code that could take a few hundreds milliseconds to execute */
   


void Task2_OtherEquipmentOperations(void *argument)

   /* The Semaphore will be given from the LPTIM Interrupt Handler, signalling that some maintenance 
      or periodic operation needs to be performed through I2C. */
   if(xSemaphoreTake(xSemaphore_LPTIMInterruptFlag, portMAX_DELAY) == pdTRUE)
   
      /* Only perform the I2C operations when the Mutex is available */
      if(xSemaphoreTakeRecursive(xMutex_I2CBus, 2000/portTICK_PERIOD_MS) == pdTRUE)
      
         /* Perform I2C Transaction */

         /* Mutex will be given back, indicating that the shared I2C Bus is now available */
         xSemaphoreGiveRecursive(xMutex_I2CBus);
      
      else
      
         /* Mutex was not available even after 2 seconds since the LPTIM interrupt triggered. 
            Perform Error Handling for the event that the I2C bus was locked */
      

      /* Piece of code that could take a few seconds to execute */
   

互斥锁是否经常用于避免优先级反转场景,或​​者它们(更经常)被广泛用于防止两个操作可能同时发生?我想不出一个简单的场景,如果一个发生优先级反转,这可能对软件至关重要。

谢谢!

【问题讨论】:

你是对的。 @MikeRobinson 人们使用互斥锁来避免优先级倒置的例子有哪些?在尝试确定优先级倒置的可能性时是否有某些指导方针/技巧?或者当更耗时的任务具有较低的优先级时,优先级反转不是一个大问题? 也许this 会帮助你? @fpiette 我已经通过诸如this 和this 之类的类似帖子研究了信号量和互斥锁的区别,但它们并没有我想要的解释。跨度> 【参考方案1】:

总的来说,我认为您的设计可以工作。信号量是两个任务完成工作的信号。并且互斥体保护共享的 I2C 资源。

但是,与互斥锁共享资源可能会导致复杂化。首先,您的操作任务在等待 I2C 资源互斥锁时不会响应新的信号量信号/事件。其次,如果应用程序变得更加复杂并且您添加了更多阻塞调用,那么设计可能会陷入阻塞、饥饿、竞争条件和死锁的恶性循环。您的简单设计尚未完成,但您正在开始一条道路。

作为替代方案,考虑让第三个任务负责处理所有 I2C 通信。 I2C 任务将等待消息(在队列或邮箱中)。当消息到达时,I2C 任务将执行相关的 I2C 通信。操作任务将像现在一样等待信号量信号/事件。但与其等待 I2C 互斥体可用,现在操作任务将向 I2C 任务发送/发布消息。使用这种设计,您不需要互斥锁来序列化对 I2C 资源的访问,因为 I2C 任务的队列/邮箱负责序列化来自其他任务的消息通信请求。同样在这个新设计中,每个任务只在一个地方阻塞,这样更干净,并且允许操作任务对信号/事件做出更好的响应。

【讨论】:

感谢您为 I2C 操作使用单独任务的建议!那么在大多数 FreeRTOS 应用程序中,通常最好在一个地方只有一个块? IE。没有两个块应该在同一个任务中?如果我需要多个块,我应该只做一个新任务,这是一个普遍的规则吗? 另外,假设您所说的场景发生了,操作任务没有响应新的信号量信号,因为它们正在等待 I2C 互斥体。在这种情况下,我假设相关的中断将被触发两次,并且来自 ISR 内部的xSemaphoreGiveFromISR() 将被调用两次。当一个二进制信号量被给出两次而没有被取走时会发生什么?这是否意味着与其关联的任务只会执行一次操作而不是两次,因为信号量的状态仍然是“可用”并且之后不会更改? 是的,我认为这是一般的经验法则,但还有更多。请参阅Beyond the RTOS: Part 2 by Miro Samek 以及他在演示文稿的幻灯片 19 和 20 上分享的参考资料。或者搜索“Active Object”,尤其是 Dobb 博士的 Herb Sutter 的文章。 是的,如果 ISR 在操作任务获得一次信号量之前向二进制信号量发送两次,那么将丢失一个事件,因为二进制信号量只计为一。您可以使用计数信号量来堆叠多个事件发生。 感谢您对这两个问题的确认!【参考方案2】:

IMO 这是一个完全错误的设计。

如果您想通知任务新数据是 - 使用队列并发布到队列。如果多个任务想要获取该数据,您可以发布到多个队列(您可以发送队列数组)。您甚至可以在运行时添加队列。

【讨论】:

感谢您的回答!我之所以认为它可以用 Mutex 实现,是因为我的印象是 Mutexes 可以用来防止多个操作可能同时发生,并且它可以用于有限的资源。我也在阅读有关互斥锁的信息,并在考虑这是否可以成为使用它的一种方式。 freeRTOS 中的 Mutex 是信号量。信号量是通过队列实现的。所以它会一样贵。 我认为互斥量与二进制信号量略有不同?是否不需要从获取 Mutex 的同一任务中提供 Mutex,而可以从多个位置(任务、ISR)提供二进制信号量?我还主要想知道什么时候肯定需要互斥体而不是二进制信号量来解决优先级反转问题,在什么情况下优先级反转将是一个大问题。 freeRTOS 中的@Cimory mutex 使用信号量作为具有优先级继承的机制。不应在中断中使用互斥锁!!!!

以上是关于一起使用二进制信号量和互斥量的主要内容,如果未能解决你的问题,请参考以下文章

一起talk C栗子吧(第一百一十六回:C语言实例--线程同步之互斥量二)

FreeRTOS系列第20篇---FreeRTOS信号量API函数

理解互斥量和信号量

FreeRTOS高级篇5---FreeRTOS队列分析

RT-Thread快速入门-互斥量

使用互斥量和信号量的屏障实现