8

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

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

功能一:

while (flag == false);
flag = false;

isr 例程:

do something
flag=true

我不记得有用于在可睡眠和不可睡眠上下文之间锁定的 linux 内核机制,例如irqkernel thread.


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

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

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

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

  4. 睡一会儿loop可以解决同步问题吗?

4

4 回答 4

12

使用volatile经常被引用为解决方案,但这并不完全正确。它通常会掩盖问题,因为volatile总是会使代码变慢。如果您的唯一用途如图所示,那么volatile可能会起作用。

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

主线:

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

伊斯尔:

/* do something */
flag=true
asm volatile ("" : : : "memory"); /* gcc barrier */

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

查看并专门搜索lock-freearm

编辑:对于补充,

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

这取决于中断源。如果它是一个定时器,并且你知道定时器源永远不会比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]

如果在xxx行期间发生第二次中断,那么您flag应该设置两次并且您错过了一次中断。障碍只是确保编译器将和ldr放在str一起。

在不同上下文之间使用障碍时的预期行为是什么?

展示的编译器内存屏障只是让编译器更快地完成工作。它在上下文之间没有影响。有不同的障碍;但它们大多用于多 CPU 设计。

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

不是真的,这只是更有效的使用。ARMWFI指令可以暂时停止CPU,这样可以节省电力。这通常是sleep()在 ARM 上所做的。如果这是一个问题,我认为您需要更改ISRmainline之间的通信。这取决于ISR来源。

于 2013-08-06T22:12:25.740 回答
0

如果您可以将标志声明为:volatile int flag;volatile bool flag;

于 2013-08-06T21:45:08.230 回答
0

这应该有助于防止错过中断。它基于来自@artless_noise 的非常详细的答案

在这里,ISR 在信号量(非阻塞调用)上发布。添加屏障以确保写入完成。该线程将运行与发布信号量一样多的次数。

sem = sem_open(argv[optind], flags, perms, 0); // Initialising semaphore to 0

function 1:

while(sem_getvalue(sem) > 0)
{
   flag = false;
   //Avoiding the barrier if this value isnt needed by ISR
   asm volatile ("" : : : "memory"); /* gcc barrier */
   //perform action
}

isr routine:
    do something
    flag=true;
    asm volatile ("" : : : "memory"); /* gcc barrier */
    sem_post(sem);
于 2019-05-15T09:21:21.953 回答
-3

是的。如果是单线程模型,则不需要锁。

于 2013-08-06T19:32:25.950 回答