Skip to content

csapp/process_demo.c ​

配套源码,运行方法见同目录 README。返回实验总览。

c
/* Block SIGCHLD before fork, atomically unblock with sigsuspend, reap in main. */
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

static volatile sig_atomic_t child_event = 0;
static void on_child(int signo) { (void)signo; child_event = 1; }
static void fail(const char *what) { perror(what); exit(1); }

int main(void) {
    sigset_t blocked, oldmask, waitmask;
    sigemptyset(&blocked);
    sigaddset(&blocked, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &blocked, &oldmask) < 0) fail("sigprocmask");
    struct sigaction action = {0};
    action.sa_handler = on_child;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_NOCLDSTOP;
    if (sigaction(SIGCHLD, &action, NULL) < 0) fail("sigaction");
    pid_t pid = fork();
    if (pid < 0) fail("fork");
    if (pid == 0) {
        if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) _exit(120);
        _exit(42);
    }
    /* A shell would add pid to its job table here, still with SIGCHLD blocked. */
    waitmask = oldmask;
    sigdelset(&waitmask, SIGCHLD);
    while (!child_event) sigsuspend(&waitmask);
    int status;
    pid_t got;
    do { got = waitpid(pid, &status, 0); } while (got < 0 && errno == EINTR);
    if (got < 0) fail("waitpid");
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) fail("restore mask");
    assert(WIFEXITED(status) && WEXITSTATUS(status) == 42);
    puts("process: child exit=42, reaped without lost wakeup");
    return 0;
}