繁体   English   中英

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

[英]How to protect a global variable shared by isr and regular function?

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

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

功能1:

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

常规:

do something
flag=true

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


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

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

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

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

  4. 在while loop睡眠可以解决同步问题吗?

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

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

主线:

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 */

这里, 屏障只是强制编译器在那时执行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]

如果在xxx行期间发生第二个中断,那么你的flag应该被设置两次而你错过了一个中断。 障碍只是确保编译器将ldrstr放在一起。

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

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

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

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

如果你可以将标志声明为: volatile int flag;会更好volatile int flag; volatile bool flag;

这应该有助于防止错过中断。 它基于来自@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);

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM