代码在优化时表现出不同的行为

Code shows different behaviour when optimized

总结

我尝试编写一个 Monte Carlo 模拟,该模拟最多可分叉到多个核心进程。一定时间后,parent 向所有 children 发送 SIGUSR1,然后应停止计算发送结果返回给 parent。

当我在没有任何优化的情况下编译 (clang thread_stop.c) 时,行为符合预期。当我尝试优化代码 (clang -O1 thread_stop.c) 时,信号被捕获,但 children 不会停止。

代码

我将代码缩减为行为相同的最小部分:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>  /* pid_t */
#include <sys/mman.h>   /* mmap */

#define MAX 1           /* Max time to run */

static int a=0; /* int to be changed when signal arrives */

void sig_handler(int signo) {
    if (signo == SIGUSR1){
        a=1;
        printf("signal caught\n");
    }
}

int main(void){

    int * comm;
    pid_t pid;

    /* map to allow child processes access same array */
    comm = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    *comm = 0;
    pid=fork();
    if(pid == 0){ /* child process */
        signal(SIGUSR1, sig_handler); /* catch signal */ 

        do {
            /* do things */
        } while(a == 0);

        printf("Child exit(0)\n");
        *comm = 2;
        exit(0); /* exit for child process */
    } /* if(pid == 0) - code below is parent only */

    printf("Started child process, sleeping %d seconds\n", MAX);
    sleep(MAX);
    printf("Send signal to child\n");
    kill(pid, SIGUSR1); /* send SIGUSR1 */
    while(*comm != 2) usleep(10000);
    printf("Child process ended\n");

/* clean up */

    munmap(comm, sizeof(int));
    return 0;
}

系统

clang 在 termux (clang 9.0.1) 和 lubuntu (clang 6.0.0-lubuntu2) 上显示了这一点。

在异步调用的信号处理程序中可以执行的操作存在限制。在您的代码中,发生这种情况是因为 kill 是从一个单独的进程调用的。

在 ISO C 中,唯一允许的可观察操作是修改 sig_atomic_t 类型的变量。

In POSIX there is a bit more leniency:

the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t, or if the signal handler calls any function defined in this standard other than one of the functions listed in the following table.

The following table defines a set of functions that shall be async-signal-safe. Therefore, applications can call them, without restriction, from signal-catching functions. Note that, although there is no restriction on the calls themselves, for certain functions there are restrictions on subsequent behavior after the function is called from a signal-catching function (see longjmp).

printf 函数不在 table 中,因此您的程序在执行信号时会导致未定义的行为(这意味着可能会出现意外结果)。


因此您需要停止在信号处理程序中调用 printf,并将 a 更改为类型 volatile sig_atomic_t

内存位置 *comm 上也存在竞争条件。一个线程读取它,而另一个线程可能同时写入它,没有同步。但是,我无法在 POSIX 文档中找到这样做的后果。

改成volatile sig_atomic_t治愈。感谢您的快速帮助。