是的你是对的。当处理程序SIGALRM
运行时,该main_loop()
函数仍在运行。这两个函数实际上将在同一个线程上运行。如果你从不退出SIGALRM
处理程序,那么下一个处理程序将进入顶部,这将一直发生,直到你用完堆栈并崩溃。
要解决此问题,只需从信号处理程序返回。
// Make sure that both of these are volatile
volatile int iteration_index;
volatile int counter;
void catch_sigalrm(int signo)
{
// Note: this is not safe, so I commented it out
// VERBOSE("Cycles :%u\n", counter);
iteration_index++;
if(iteration_index == iterations)
exit(0);
counter = 0;
set_alarm();
}
void main_loop(void)
{
set_alarm();
while (1)
__sync_fetch_and_add(&counter, 1);
}
我认为这会奏效。它应该使用ldrex
andstrex
来增加counter
.
使用的问题counter++
是它可以被中断:counter++
实际上是这样的:
int old_value = counter; // memory access
int new_value = old_value + 1;
counter = new_value; // memory access
如您所见,如果警报在中间响起,counter++
结果将被清除。这就是为什么你需要使用它__sync_fetch_and_add()
。
说明volatile
符是强制性的
这是带有普通计数器变量的主循环。我已经删除了对的调用,set_alarm()
因为我们现在不关心它。
// This is wrong
int counter;
void main_loop()
{
while (1)
counter++;
}
这是组装:
_main_loop:
b _main_loop
.comm _counter, 4, 2
等一等!它不会增加任何东西,它只是一个无限循环!
这是正确的。编译器检测到counter
不可能在任何地方读取,因此优化了不存在的增量。你必须使用volatile
. volatile
关键字指示编译器counter
可以由非线程的其他东西读取或写入(实际上,它指示编译器对加载和存储严格,但这是技术版本)。(对于使用线程,volatile
几乎没有用处,您必须使用不同的原子。)
这是带有的版本volatile
:
// This is ALSO wrong
volatile int counter;
void main_loop(void)
{
while (1) counter++;
}
和大会:
_main_loop:
; load address of counter into r0
...
loop:
; increment counter
ldr r1, [r0]
adds r1, #1
str r1, [r0]
b loop
如您所见,这可以在中间中断。这就是您必须使用的原因__sync_fetch_and_add()
:它检测何时counter++
被中断并从头开始重新启动操作。
这是正确的版本:
// MUST be volatile
volatile int counter;
void main_loop(void)
{
while (1)
__sync_fetch_and_add(&counter, 1);
}
更多笔记
- 您不能调用
printf()
信号处理程序。
- 事实上,尽量避免在信号处理程序中做任何事情。
- 最多设置一个标志,将一个字节写入管道,或类似的东西。