我需要在我的代码中进行数据同步。目前,我正在访问中断内部的全局值,以及如果频繁调用中断可能会破坏数据的本地函数。我需要避免这种情况。我没有在我的代码中使用操作系统,所以我不能使用信号量。使用与信号量类似的锁定方法可能会解决我的问题。
任何帮助,将不胜感激
我需要在我的代码中进行数据同步。目前,我正在访问中断内部的全局值,以及如果频繁调用中断可能会破坏数据的本地函数。我需要避免这种情况。我没有在我的代码中使用操作系统,所以我不能使用信号量。使用与信号量类似的锁定方法可能会解决我的问题。
任何帮助,将不胜感激
中断的工作方式与线程或进程不同 - 如果线程等待信号量,则在信号量可用或等待超时过去之前不会对其进行调度。同时,可以调度其他线程,其中一个线程可能会返回信号量。
中断服务例程不是这种情况——它们不会被任何线程调度中断(如果有的话,那么只会被其他中断),但会一直执行到它们返回。因此,如果 ISR 等待信号量(或您要求的类似机制),我们将陷入死锁,因为无法再调度持有它的线程来返回信号量......
所以你需要一个完全不同的机制!
执行此操作的常用方法是,只要您的函数需要访问公共数据,就禁用中断,然后重新启用它(您也可能需要在 ISR 本身内执行此操作)。
如何?好吧,特定于操作系统/硬件-只要您不提供更多详细信息,我就在这里...
只是一些提示:保持禁用中断的时间尽可能短,并确保经常访问的数据被声明为易失性!
在您的主代码中可能就像这样简单:
disable_interrupts();
value += 1;
enable_interrupts();
因此,当您使用主代码中的值时,请确保中断不会触发。
您需要的是对数据的原子访问。如果它是单个变量并且您可以保证访问是原子的,那么这就足够了。但是,这涉及反汇编 C 代码并查看最终结果。即使机器代码最终成为原子(单指令),它也不会是可移植的。
如果您有一个支持 C11 的现代编译器,您可以将共享变量声明为_Atomic
,这将解决问题。
另一种选择是在调用者的变量访问期间简单地关闭特定中断。但是,这会破坏实时性能,您可能会错过中断。
普遍“最佳”的解决方案可能是自己发明一个信号量。例子:
// volatile to prevent dangerous compiler optimizations; does not solve re-entrancy
volatile uint32_t data;
volatile bool guard;
void ISR (void)
{
if(!guard)
{
data = SOME_REGISTER;
}
}
void main (void)
{
...
guard = true;
uint32_t local = data;
guard = false;
}
在上面的示例中,根本不保证原子访问,甚至对guard
变量也不保证。但是,它不再需要,因为在main()
即将读取数据的点,guard
保证设置。如果在读取期间中断会启动,它不会破坏数据。
此解决方案的唯一缺点是设置保护时您将错过更新data
。如果这是一个问题,您将不得不实施某种方式的临时存储。
(请注意,此代码不会导致“内存屏障”,因此在复杂的多核处理器上,此方法可能不起作用,volatile
也不一定会导致内存屏障。但在普通微控制器上它可以正常工作。)