温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c - Code shows different behaviour when optimized
c clang fork optimization

c - 优化后的代码显示不同的行为

发布于 2020-04-09 11:15:11

摘要

我想编写一个蒙特卡洛仿真代码,该仿真可以插入最多数量的内核进程。一定时间后,父级将SIGUSR1发送给所有子级,然后子级应停止计算返回给父级的发送结果。

当我不进行任何优化(clang thread_stop.c)进行编译时,其行为符合预期。当我尝试优化代码(clang -O1 thread_stop.c)时,会捕获信号,但子级不会停止。

我将代码缩减为行为相同的最小片段:

#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)上显示了这一点。

查看更多

提问者
Florian
被浏览
64
M.M 2020-02-01 08:59

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

在ISO C中,唯一允许的可观察动作是修改type的变量sig_atomic_t

在POSIX中有更多的宽大处理

如果信号处理程序引用了具有静态存储持续时间的errno以外的任何对象,而不是通过为声明为volatile sig_atomic_t的对象分配值,则该行为未定义,或者如果信号处理程序调用了该标准中定义的任何函数,而不是下表中列出的功能。

下表定义了一组应为异步信号安全的功能。因此,应用程序可以不受限制地从信号捕获功能中调用它们。请注意,尽管对调用本身没有限制,但是对于某些函数,从信号捕获函数调用该函数后,其后续行为受到限制(请参见longjmp)。

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


因此,您将需要停止调用printf信号处理程序,并将其更改a为具有type volatile sig_atomic_t

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