在事件驱动的嵌入式系统中使用事件队列避免竞争条件

Posted

技术标签:

【中文标题】在事件驱动的嵌入式系统中使用事件队列避免竞争条件【英文标题】:Avoiding Race Condition with event queue in event driven embedded system 【发布时间】:2021-03-24 20:08:38 【问题描述】:

我正在尝试对 stm32 进行编程并使用事件驱动架构。例如,当发生定时器中断时,我将切换一个引脚,并在发生 ADC DMA 缓冲区满中断时将一些数据传输到外部闪存等等..

会有多个中断源,每个中断源都具有相同的优先级,从而禁用嵌套。

我将使用中断设置一个标志来通知我的 main 发生了中断并在 main 中处理数据。 ISR 内部不会有处理/指令。

困扰我的是,从长远来看,在 main 和 ISR 中访问变量(在这种情况下为标志)可能会导致竞态条件错误。

所以我想使用循环事件队列而不是标志。

只有 ISR 能够写入事件队列缓冲区并增加“头”。 只有 main 才能读取事件队列(并根据事件执行指令)并增加“tail”。

由于禁用了 ISR 嵌套,每个 ISR 将访问事件队列数组的不同元素,并且 main 函数只会在事件队列上有新事件时做出反应,所以避免了竞争条件对吧?还是我错过了什么?

如果我做错了什么,请纠正我。

谢谢。

【问题讨论】:

这不是真正的多线程环境,因此竞争条件可能只适用于主代码和 ISR 之间。但是通过在主代码的关键部分禁用中断很容易解决。 Do volatile int flag; [global] 并在main 中执行:int copy; cli(); copy = flag; flag = 0; sti(); if (copy) ... ISR 可以根据需要简单地设置flag 您已经足够清楚地解释了解决方案,但您并没有真正解释您要解决的问题。描述将导致错误的竞争条件场景,因为不清楚您的意思。您可以使用位带以原子方式读取和清除标志,而不是使用事件标志的读取-修改-写入,或使用单独的布尔值。所有拥有队列都可以,您将按照处理中断的顺序进行处理,但这是不确定的,因为您禁用了抢占。听起来你也发明了一个假设问题。 听起来不错。我经常这样做,但使用一个简单的多线程任务器,我可以在其中发出信号量以指示事件已加载到队列中,因此需要 I/O 处理程序线程的注意:) @EugeneSh。我在某处读到启用和禁用中断会导致中断响应时间大大延迟。我不知道它是否属实,但如果是这样,我需要快速响应中断。 【参考方案1】:

如果中断只设置了一个变量,并且在主上下文准备好之前什么都不做,那么根本就没有理由产生中断。

例如:如果你得到一个 DMA 完整的硬件中断并设置一个变量,那么你所做的只是将一位信息从硬件寄存器复制到一个变量。通过轮询变量而不是不启用中断并直接轮询硬件标志,您可以得到具有相同性能和更少出错可能性的更简单的代码。

仅当您实际上要在无法等待的中断上下文中执行某些操作时才启用中断,例如:读取 UART 接收到的数据寄存器,以便接收到的下一个字符不会溢出缓冲区。

如果中断完成后不能等待的事情然后需要与主上下文进行通信,那么您需要共享数据。这意味着您需要某种方法来防止竞争条件。最简单的方法是原子访问,只有一侧写入数据项。如果这还不够,那么老式的方法是在主上下文访问共享数据时关闭中断。使用 LDREX/STREX 指令有更复杂的方法,但只有在确定简单的方法对您的应用程序不够好时,您才应该探索这些方法。

【讨论】:

如果为了省电而无事可做,CPU 将进入睡眠状态,因此我必须使用中断来唤醒 CPU。所以我不能在 main 方法中使用轮询。

以上是关于在事件驱动的嵌入式系统中使用事件队列避免竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

仅在Rust中编写的软件能完全避免竞争条件吗?

Linux设备驱动之input子系统

如何避免 VxWorks 中条件变量中的竞争条件

基于C++11的事件驱动框架

基于C++11的事件驱动框架

JavaScript 事件处理的竞争条件?