Код показывает другое поведение при оптимизации - PullRequest
0 голосов
/ 01 февраля 2020

Сводка

Я пытался закодировать симуляцию Монте-Карло, которая включает в себя до нескольких основных процессов. Через некоторое время родитель отправляет 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).

Ответы [ 2 ]

1 голос
/ 01 февраля 2020

Существуют ограничения на то, что вы можете делать в обработчике сигналов, который вызывается асинхронно. В вашем коде это происходит потому, что kill вызывается из отдельного процесса.

В ISO C единственным допустимым наблюдаемым действием является изменение переменной типа sig_atomic_t.

В POSIX есть немного больше снисходительности :

поведение не определено, если обработчик сигнала ссылается на любой объект, отличный от errno с stati c продолжительность хранения, кроме как путем присвоения значения объекту, объявленному как volatile sig_atomic_t, или если обработчик сигнала вызывает любую функцию, определенную в этом стандарте, кроме одной из функций, перечисленных в следующей таблице.

В следующей таблице определяется набор функций, которые должны быть asyn c -signal-safe. Поэтому приложения могут вызывать их без ограничений из функций сбора сигналов. Обратите внимание, что, хотя нет никаких ограничений на сами вызовы, для некоторых функций существуют ограничения на последующее поведение после вызова функции из функции перехвата сигнала (см. Longjmp).

printf функция отсутствует в таблице, поэтому ваша программа вызывает неопределенное поведение при выполнении сигнала (что означает, что могут последовать непредвиденные результаты).


Поэтому вам нужно прекратить вызывать printf в сигнале обработчик, а также измените a на тип volatile sig_atomic_t.

Существует также состояние гонки в ячейке памяти *comm. Один поток читает его, в то время как другой может записывать его одновременно, без синхронизации. Однако я не смог найти в документации POSIX, каковы последствия этого.

0 голосов
/ 01 февраля 2020

Изменение на volatile sig_atomic_t вылечить. Спасибо за быструю помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...