2

我目前正在使用警报信号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: call main_loop
- Else exit

我实现了上述逻辑,发现它在几千次迭代后出现了段错误。我相信这样做的原因是:
当警报触发并重新调用时main_loop,原始main_loop框架仍然存在。这种情况不断重复发生,直到空间不足并引发段错误。

我已经尝试提出一些可以满足我要求的设计,但是在触发信号后我无法可视化代码流。
实现我所描述的正确方法是什么?

4

2 回答 2

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

我认为这会奏效。它应该使用ldrexandstrex来增加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()信号处理程序。
  • 事实上,尽量避免在信号处理程序中做任何事情。
  • 最多设置一个标志,将一个字节写入管道,或类似的东西。
于 2013-11-01T20:02:55.160 回答
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; 
}
于 2013-11-01T19:35:12.573 回答