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

时间:2013-08-06 19:31:23

标签: c assembly arm mutex semaphore

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

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

功能1:

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

isr例程:

do something
flag=true

我不记得有一个用于在可睡眠和非可睡眠环境之间进行锁定的Linux内核机制,例如: irqkernel thread


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

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

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

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

  4. loop期间睡眠可以解决同步问题吗?

4 个答案:

答案 0 :(得分:10)

经常引用volatile作为解决方案,但事实并非如此。它通常会掩盖问题,因为volatile将始终使代码变慢。如果您的唯一用途如图所示,那么volatile可能会有效。

使用单个阅读器单个写来使用memory barriers可能会更好。那将是你的代码,

<强>干线:

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

<强> ISR:

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

此处, barrier 仅强制编译器在此时执行 ARM str指令。优化器不会在之前或之后移动任何代码。您还可以使用swpldrexstrex,具体取决于您的 ARM CPU。同样,环形缓冲区通常与 ISR mainlines 一起使用,因为它们不需要任何特殊的CPU支持;只有编译器内存屏障

请参阅并专门搜索lock-free and arm

修改:有关添加内容,

  

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

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

  

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

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

  1: ldr r0, [r1]
     cmp r0, #0    ; xxx
     bne 1b        ; xxx
     mov r0,#1     ; xxx
     str r0, [r1]

如果在 xxx 行期间发生第二次中断,则应将flag设置为两次并错过一次中断。 障碍只是确保编译器将ldrstr放在一起。

  

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

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

  

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

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

答案 1 :(得分:0)

如果您可以将旗帜声明为:volatile int flag;volatile bool flag;

,那会更好

答案 2 :(得分:0)

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

此处,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);

答案 3 :(得分:-2)

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