我想编写一个蒙特卡洛仿真代码,该仿真可以插入最多数量的内核进程。一定时间后,父级将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)上显示了这一点。
对于在异步调用的信号处理程序中可以执行的操作有一些限制。在您的代码中发生这种情况是因为kill
是从一个单独的进程中调用的。
在ISO C中,唯一允许的可观察动作是修改type的变量sig_atomic_t
。
如果信号处理程序引用了具有静态存储持续时间的errno以外的任何对象,而不是通过为声明为volatile sig_atomic_t的对象分配值,则该行为未定义,或者如果信号处理程序调用了该标准中定义的任何函数,而不是下表中列出的功能。
下表定义了一组应为异步信号安全的功能。因此,应用程序可以不受限制地从信号捕获功能中调用它们。请注意,尽管对调用本身没有限制,但是对于某些函数,从信号捕获函数调用该函数后,其后续行为受到限制(请参见longjmp)。
该printf
函数不在表中,因此在执行信号时,程序会导致未定义的行为(这意味着可能会发生意外的结果)。
因此,您将需要停止调用printf
信号处理程序,并将其更改a
为具有type volatile sig_atomic_t
。
内存位置上也存在争用条件*comm
。一个线程读取它,而另一个线程可以同时写入它,而没有同步。但是,我无法在POSIX文档中找到此问题的后果。