如何保护通过ISR​​和常规功能共享的全局变量? [英] How to protect a global variable shared by isr and regular function?

查看:165
本文介绍了如何保护通过ISR​​和常规功能共享的全局变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们说我有功能1 ISR常规,两者共享和更新相同的标志,没有任何锁它们之间。 系统是单线程的。

在同时会有3个臂的汇编指令,这意味着它不是原子操作,它是确定共享非ISR和ISR功能之间的全局变量没有任何锁或保护?

功能1:

 而(标志==假);
标志= FALSE;

ISR程序:

 做点什么
标志=真


我不记得有用于可休眠和非休眠式上下文之间的锁定例如Linux内核机制 IRQ 内核线程


他的回答感谢@artless这里有一些问题,我不知道:


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


  2. 记忆障碍,如何解决这个问题,它有效果时,code在单CPU的运行速度?


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


  4. 可以在同时休眠循环就可以解决同步问题?



解决方案

使用挥发性经常被引述作为一个解决方案,但是这并不完全正确。它往往会掩盖一个问题,因为挥发性总会让code慢。如果如你唯一的使用,那么挥发性可能会工作。

这可能是更好用的单读的和的单写的使用的内存屏障的。这将是你的code,那么,

主线:

 挥发为int * p =&放大器;标志;
而(* P ==假); / *如果你查询*您必须使用挥发性/
标志= FALSE;
ASM挥发性(:内存); / * GCC屏障* /

ISR:

  / *做某事* /
标志=真
ASM挥发性(:内存); / * GCC屏障* /

在这里,屏障的只是强制编译器做的 ARM STR 指令在这一点上。之前或之后的优化器将无法移动code。您还可以使用 SWP LDREX STREX 视您的 ARM CPU。同时,环缓冲区往往与 ISR 的使用,主线的,因为他们不需要任何特殊的CPU的支持;只有编译器的内存屏障

查看无锁特别搜索无锁的和的手臂

编辑::用于添加,


  

有没有办法我不会想念中断?


这取决于中断源。如果它是一个计时器,你的知道的定时器源不能超过 XX 的说明,也没有其他中断都活跃在系统中,那么你的当前code会更快工作。但是,如果中断是从类似于外部源的<青霉>以太网的控制器,非去抖键盘等,这是可能的多个中断迅速到来。有些时候,新的中断,即使中断处理程序时发生。根据在ISR源上,有不同的解决方案。 A 环形缓冲区的是常用的,从队列工作项的 ISR 作为主线。对于 UART ,环可能包含实际的字符数据。这可能是指针列表等,这是很难同步的 ISR 主线当通信变得更加复杂;所以我相信,答案取决于中断源。这就是为什么每个操作系统的有这么多的原语和基础结构此问题。


  

记忆障碍,如何解决这一问题,它在code在单CPU的运行速度有效果吗?


记忆障碍并不彻底解决无缘中断问题;就像挥发性没有。他们只是做的窗口的要小得多。他们强迫编译器的进度的负载或更早的商店。例如主线循环,

  1:LDR R0,[R1]
     CMP R0,#0; XXX
     BNE 1B; XXX
     MOV R0,#1; XXX
     STR R0,[R1]

如果期间的 XXX 的行的第二个中断发生,那么你的标志应设置两次,你错过了一个中断。在壁垒的只是确保编译器会将 LDR STR 并拢。


  

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


编译器的内存屏障的我秀只是让编译器的做的东西的越快。它具有上下文之间没有影响。有不同的壁垒的;但大多是一些对多CPU设计。


  

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


不是真的,这仅仅是一个更有效的利用。 ARM的 WFI 指令可以暂时停止的CPU和这将节省电能。通常是的睡眠()的确实的手臂。我想,你需要改变的 ISR 之间的沟通和在主线,如果这是一个问题。这取决于在 ISR 来源。

Let's say I have function 1 and an isr routine, both share and update the same flag without any lock between them. the system is single threaded.

the while will be a 3 arm assembly instructions, which means it is not atomic operation, is it ok to share a global variable between non isr and isr functions without any lock or protection?

function 1:

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

isr routine:

do something
flag=true


I don't remember there is a linux kernel mechanism for locking between sleepable and non sleepable context e.g. irq and kernel thread.


Thanks @artless for his answer here are some issues I am not sure about:

  1. Is there a way I won't miss interrupt at all ?

  2. How the memory barriers solve the issue, does it have effect when the code runs on single cpu ?

  3. What is the expected behavior when using barriers between different contexts ?

  4. Can a sleep in the while loop can solve problems of syncs ?

解决方案

Using volatile is often quoted as a solution, but this is not quite true. It will often mask a problem as volatile will always make code slower. If your only use is as shown, then volatile will probably work.

It is probably better with a single reader and single write to use memory barriers. This would be your code then,

Mainline:

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

Here, the barrier just forces the compiler to do the ARM str instruction at that point. The optimizer will not move any code before or after. You can also use swp or ldrex and strex depending on your ARM CPU. As well, ring buffers are often used with ISR and mainlines as they don't need any special CPU support; only the compiler memory barrier.

See the and specifically search lock-free and arm.

Edit: For additions,

Is there a way I won't miss interrupt at all ?

This is dependent on the interrupt source. If it is a timer and you know the timer source can never be faster than XX instructions and no other interrupts are active in the system, then your current code will work. However, if the interrupt is from an external source like an Ethernet controller, a non-debounced keypad, etc. It is possible for multiple interrupts to come quickly. Some times new interrupts even happen during the interrupt handler. Depending on the ISR source, there are different solutions. A ring buffer is commonly used to queue work items from the ISR for the main line. For a UART, the ring might contain actual character data. It could be a list of pointer, etc. It is difficult to synchronize the ISR from the mainline when the communication becomes more complex; So I believe the answer depends on the interrupt source. This is why every OS has so many primitives and infra-structure for this issue.

How the memory barriers solve the issue, does it have effect when the code runs on single cpu ?

Memory barriers don't completely solve the missed interrupt issue; just like volatile doesn't. They just make the window much smaller. They force the compiler to schedule a load or store earlier. For example the main line loop,

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

If a 2nd interrupt happens during the xxx lines, then your flag should be set twice and you missed one interrupt. The barriers just make sure the compiler places the ldr and str close together.

What is the expected behavior when using barriers between different contexts?

The compiler memory barrier I show just makes the compiler do stuff sooner. It has no effect between contexts. There are different barriers; but mostly they are for multi-CPU designs.

Can a sleep in the while loop can solve problems of syncs?

Not really, this is just a more efficient use. The ARM WFI instruction can temporarily stop the CPU and this will save power. That is normally what sleep() does on the ARM. I think you need to change the communication between the ISR and the mainline, if this is an issue. That depends on the ISR source.

这篇关于如何保护通过ISR​​和常规功能共享的全局变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆