我目前正在使用警报信号SIGALRM
退出无限循环。
我的代码结构如下
main_loop() {
set_alarm();
while(1)
counter++;
}
逻辑是
- 设置闹钟
- 输入while循环
- 当我们得到SIGALRM
在SIGALRM
上运行的代码如下:
VERBOSE("Cycles :%u\n", counter);
iteration_index++;
if(iteration_index == iterations)
exit(0);
counter = 0;
main_loop();
我现在想给用户一个选项,指定闹钟响应的次数(iterations
)。简而言之,将上述逻辑修改为:
- 设置闹钟
- 输入while循环
- 当我们得到SIGALRM
时,阅读计数器
- 增加iteration_index
- If iteration_index < iterations
:致电main_loop
- 其他退出
我实现了上面的逻辑,发现它经过几千次迭代后就会出现段错误。我认为其原因是:
当警报触发并对main_loop
进行新呼叫时,原始main_loop
帧仍然存在。这种情况不断发生,直到空间不足并引发段错误。
我尝试过一些可以实现我想要的设计,但是在触发信号后我无法看到代码流。
实施我所描述的方法的正确方法是什么?
答案 0 :(得分:0)
重新列出您列出的修改:
- Set an alarm
- Enter while loop
- Read counter when we get SIGALRM
- Increment iteration_index
- If iteration_index < iterations: call main_loop
- Else exit
您可以提供一系列击键(例如<ctrl> - 1
)以允许用户指定多次(仅一次)。此示例运行(延迟时间允许GetAsyncKeys()
。直到用户按下<ctrl> 1
,循环将永远运行。当按下<ctrl> 1
时,程序会提示他们有多少时间让闹钟响起,然后程序运行那么多次迭代,然后退出..
#include <stdio.h>
#include <windows.h>
void set_alarm(void);
int main(void) {
int iterations=-1, counter=0;
while (iterations != counter)
{
if(iterations == -1)//code will run forever if iteration is not set by user
{
set_alarm();
counter++;
if (GetAsyncKeyState(VK_CONTROL)<0)
{
if (GetAsyncKeyState('1')<0)
{
printf("Enter how many time alarm should activate:\n");
scanf("%d", &iterations);
counter = 0;
Sleep(10);
}
}
}
else//this block will monitor alarm count, program quits when limit reached.
{
if(counter < iterations)
{
set_alarm();
counter++;
}
}
Sleep(10);//changed here for my test, you might need to change again
}
return 0;
}
void set_alarm(void)
{
//do something;
}
答案 1 :(得分:0)
是的,你是对的。当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
和strex
来增加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
说明符是MANDATORY 这是带有普通计数器变量的主循环。我已删除了对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()
。