在报警时多次调用无限循环代码

时间:2013-11-01 18:54:50

标签: c linux unix infinite-loop

我目前正在使用警报信号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帧仍然存在。这种情况不断发生,直到空间不足并引发段错误。

我尝试过一些可以实现我想要的设计,但是在触发信号后我无法看到代码流。
实施我所描述的方法的正确方法是什么?

2 个答案:

答案 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);
}

我认为这会奏效。它应该使用ldrexstrex来增加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无法在任何地方读取,因此优化了增量。你必须使用volatilevolatile关键字指示编译器可以通过读取或写入不是线程的其他来编写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()
  • 事实上,尽量避免在信号处理程序中执行任何
  • 最多设置一个标志,将一个字节写入管道或类似的东西。