[英]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内核机制来锁定可睡眠和非可睡眠的上下文,例如irq
和kernel thread
。
谢谢@artless的答案,这里有一些我不确定的问题:
有没有办法我不会错过中断?
内存障碍如何解决问题,当代码在单个cpu上运行时它是否有效?
在不同情境之间使用障碍时的预期行为是什么?
在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
指令。 优化器不会在之前或之后移动任何代码。 您也可以使用swp
或ldrex
和strex
具体取决于您的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
应该被设置两次而你错过了一个中断。 障碍只是确保编译器将ldr
和str
放在一起。
在不同情境之间使用障碍时的预期行为是什么?
编译器内存屏障我秀只是让编译器做的东西越快。 它在上下文之间没有影响。 有不同的障碍 ; 但主要是用于多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.