如何保护isr和常规函数共享的全局变量?

0x9*_*x90 6 c assembly mutex arm semaphore

假设我有function 1一个isr routine,它们共享并更新相同的标志,它们之间没有任何锁定.系统是单线程的.

while将是一个3臂汇编指令,这意味着它不是原子操作,是否可以在非isr和isr函数之间共享一个全局变量而没有任何锁定或保护?

功能1:

while (flag == false);
flag = false;
Run Code Online (Sandbox Code Playgroud)

常规:

do something
flag=true
Run Code Online (Sandbox Code Playgroud)

我不记得有一个Linux内核机制用于锁定可睡眠和非可睡眠的上下文,例如irqkernel thread.


谢谢@artless的答案,这里有一些我不确定的问题:

  1. 有没有办法我不会错过中断?

  2. 内存障碍如何解决问题,当代码在单个cpu上运行时它是否有效?

  3. 在不同情境之间使用障碍时的预期行为是什么?

  4. 能在一段时间内睡觉loop可以解决同步问题吗?

art*_*ise 10

使用volatile经常被引用作为解决方案,但这不是真的.它经常掩盖问题,因为volatile总是会使代码变慢.如果您的唯一用途如图所示,那么volatile可能会有效.

使用单个读取器单个写入可能更好地使用内存屏障.这将是你的代码,

主线:

volatile int *p = &flag;
while (*p == false);   /* You must use volatile if you poll */
flag = false;
asm volatile ("" : : : "memory"); /* gcc barrier */
Run Code Online (Sandbox Code Playgroud)

ISR:

/* do something */
flag=true
asm volatile ("" : : : "memory"); /* gcc barrier */
Run Code Online (Sandbox Code Playgroud)

这里,屏障只强制编译器在那时执行ARM str指令.优化器不会在之前或之后移动任何代码.您还可以使用swpldrexstrex根据您的ARM CPU.同样,环形缓冲区通常与ISR主线一起使用,因为它们不需要任何特殊的CPU支持; 只有编译器内存屏障.

看到无,专门搜索无手臂.

编辑:添加,

有没有办法我不会错过中断?

这取决于中断源.如果它是一个计时器,并且您知道计时器源永远不会比XX指令快,并且系统中没有其他中断处于活动状态,那么您当前的代码将起作用.但是,如果中断来自外部源,如以太网控制器,非去抖键盘等,则可能会很快出现多个中断.有时在中断处理程序期间甚至会发生新的中断.根据ISR来源,有不同的解决方案.甲环缓冲器通常用于排队从所述工作项目ISR对于主线.对于UART,环可能包含实际字符数据.它可以是指针列表等.当通信变得更复杂时,很难从主线同步ISR ; 所以我认为答案取决于中断源.这就是为什么每个操作系统都有这么多原语和基础设施来解决这个问题.

内存障碍如何解决问题,当代码在单个cpu上运行时它是否有效?

内存障碍并不能完全解决错过的中断问题; 就像volatile没有.他们只是让窗户小得多.它们强制编译器提前安排加载或存储.例如主线循环,

  1: ldr r0, [r1]
     cmp r0, #0    ; xxx
     bne 1b        ; xxx
     mov r0,#1     ; xxx
     str r0, [r1]
Run Code Online (Sandbox Code Playgroud)

如果在xxx行期间发生第二次中断,那么你flag应该设置两次并且你错过了一次中断.该障碍只是确保编译器将ldrstr并拢.

在不同情境之间使用障碍时的预期行为是什么?

编译器内存屏障我秀只是让编译器做的东西越快.它在上下文之间没有影响.有不同的障碍 ; 但主要是用于多CPU设计.

在while循环中睡眠可以解决同步问题吗?

不是,这只是一种更有效的用途.ARM WFI指令可以暂时停止 CPU,这样可以节省电量.这通常是sleep()在ARM上的作用.如果这是一个问题,我认为您需要更改ISR主线之间的通信.这取决于ISR来源.