为什么我不能在此处理程序中更新全局sig_atomic_t?

luaexgnf  于 2023-08-03  发布在  其他
关注(0)|答案(2)|浏览(84)

下面是C代码:

// my_repro.c

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

volatile sig_atomic_t counter = 1;

void handler(int sig) {
    counter = 15;
    return;
}

int main(void) {
    pid_t pid;
    printf("%d\n", counter);
    fflush(stdout);

    signal(SIGINT, handler);
    if ((pid = fork()) == 0) {
        while(1) {};
    }

    kill(pid, SIGINT);
    printf("%d\n", ++counter);
    fflush(stdout);
}

字符串
我编译:第一个月
并运行:./my_repro.c
我用的是x86-64的Mac。
为什么它会输出:

1
2


而不是:

1
16



我认为对全局变量使用volatile sig_atomic_t的目的是为了让信号处理程序可以改变这些全局变量的状态。

cigdeys3

cigdeys31#

sig_atomic_t需要存储在共享内存段中,以便在父进程和子进程之间共享。在父节点向子节点发送信号之前,需要进行一些仔细的同步。下面的示例代码在派生子进程之前阻塞信号,并让子进程调用sigsuspend来解除阻塞信号并等待它。在父节点向子节点发送信号之后,还需要进行一些同步。下面的示例代码只是让子进程在捕获信号后正常退出,父进程等待子进程终止,然后再次使用共享的sig_atomic_t。如果希望保持子进程运行,则可以使用更复杂的同步机制。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/wait.h>

volatile sig_atomic_t *counterp;

void handler(int sig) {
    *counterp = 15;
}

int main(void) {
    enum { MYSIG = SIGUSR1 };    /* Choose the signal to use here. */
    int shmid;      /* shared memory identifier */
    struct shared { /* layout of shared memory */
        sig_atomic_t counter;
    };
    struct shared *shm;     /* pointer to attached shared memory segment */
    static const struct sigaction action = {
        .sa_handler = handler,
    };
    sigset_t blocked;
    sigset_t oldset;
    pid_t pid;
    pid_t wpid;
    int wstatus;
    int rc;

    /* Create a shared memory segment. */
    shmid = shmget(IPC_PRIVATE, sizeof *shm, IPC_CREAT | 0666);
    if (shmid < 0){
        perror("an error in shmget");
        exit(EXIT_FAILURE);
    }

    /*
     * Attach shared memory segment to process address space.
     *
     * The attachment will be inherited by child processes.
     */
    shm = shmat(shmid, NULL, 0);
    if(shm == (void *)-1){
        perror("an error in shmat");
        exit(EXIT_FAILURE);
    }

    /*
     * Point to the sig_atomic_t in the shared memory segment and set a value.
     */
    counterp = &shm->counter;
    *counterp = 1;

    printf("%d\n", (int)*counterp);
    fflush(stdout);

    /* Block the MYSIG signal before forking. */
    rc = sigemptyset(&blocked);
    if (rc == -1) {
        perror("an error in sigemptyset");
        exit(EXIT_FAILURE);
    }
    rc = sigaddset(&blocked, MYSIG);
    if (rc == -1) {
        perror("an error in sigaddset");
        return EXIT_FAILURE;
    }
    rc = sigprocmask(SIG_BLOCK, &blocked, &oldset);
    if (rc == -1) {
        perror("an error in sigprocmask");
        exit(EXIT_FAILURE);
    }
    
    pid = fork();
    if (pid == -1) {
        perror("an error in fork");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        /* Child process.  Set signal handler. */
        rc = sigaction(MYSIG, &action, NULL);
        if (rc == -1) {
            perror("an error in sigaction");
            exit(EXIT_FAILURE);
        }
        /* Unblock and wait for signal. */
        sigsuspend(&oldset);
        /* sigsuspend always returns -1 and normally sets errno = EINTR. */
        if (errno != EINTR) {
            perror("an error in sigsuspend");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    } else {
        /* Parent process.  Unblock signal. */
        rc = sigprocmask(SIG_SETMASK, &oldset, NULL);
        if (rc == -1) {
            perror("an error in sigprocmask");
            exit(EXIT_FAILURE);
        }
        /* Send a signal to child process. */
        rc = kill(pid, MYSIG);
        if (rc == -1) {
            perror("an error in kill");
            exit(EXIT_FAILURE);
        }
        /* Wait for child process to terminate. */
        wpid = wait(&wstatus);
        if (wpid == -1) {
            perror("an error in wait");
            exit(EXIT_FAILURE);
        }
        if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != EXIT_SUCCESS) {
            fprintf(stderr, "child exited with non-zero exit status %d\n",
                    WEXITSTATUS(wstatus));
            exit(EXIT_FAILURE);
        }
        if (WIFSIGNALED(wstatus)) {
            fprintf(stderr, "child terminated by %s\n",
                    strsignal(WTERMSIG(wstatus)));
            exit(EXIT_FAILURE);
        }
        printf("%d\n", (int)++*counterp);
        fflush(stdout);
        exit(EXIT_SUCCESS);
    }
    return EXIT_SUCCESS;
}

字符串

iqjalb3h

iqjalb3h2#

看起来问题是分叉进程获得所有变量的副本,* 包括 * 全局变量。如果我这样做:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

volatile sig_atomic_t counter = 1;

void handler1(int sig) {
    counter = 15;
    return;
}

int main(void) {
    pid_t pid;
    printf("%d\n", counter);
    fflush(stdout);

    // Register the signal handler on the parent process
    signal(SIGINT, handler1);

    kill(getpid(), SIGINT);
    printf("%d\n", ++counter);
    fflush(stdout);
}

字符串
然后我得到

1
16


如所料。

相关问题