简体   繁体   中英

How can I wait for both a file-descriptor and child state change simultanously?

In Linux, one can wait on any FD using select , poll or epoll . It is also possible to wait for child-processes to change state using wait , waitpid or waitid . However, I can't figure a way to combine these operations, ie, to block the calling process until either some FD becomes ready or a child process changes state.

I can use polling, by repeatedly calling non-blocking epoll then waitid , but that is wasteful.

It is possible to create a pidfd for a child process (which is accepted by epoll ), but pidfd only supports waiting for child termination , while I wish to wait for any state change (specifically, for ptrace stops).

Is this not possible in Linux?

You can wait for any child status change with signalfd() and make dummy read, then get actual status with waitpid():

sigset_t mask, old_set;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &old_set);

int sigfd = signalfd(-1, &mask, SFD_CLOEXEC);
if (sigfd == -1) {
    perror("signalfd");
    return 1;
}

for (int i = 0; i < 10; ++i) {
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
    }
    if (pid == 0) {
        // Child process: restore blocked signals before exec() etc
        sigprocmask(SIG_SETMASK, &old_set, NULL);
        sleep(i % 3);
        switch (i % 3) {
            case 0:
                raise(SIGSTOP);
                break;
            case 1:
                raise(SIGABRT);
                break;
        }
        exit(i);
    }
    printf("Spawned child %i with pid %u\n", i, pid);
}

for (;;) {
    struct pollfd fds[] = {
        { .fd = STDIN_FILENO, .events = POLL_IN },
        { .fd = sigfd,        .events = POLL_IN }
    };
    if (poll(fds, sizeof(fds)/sizeof(*fds), -1) == -1) {
        perror("poll");
        break;
    }

    if (fds[0].revents & POLL_IN) {
        char buf[4096];
        int ret = read(STDIN_FILENO, buf, sizeof(buf));
        printf("Data from stdin: ");
        fflush(stdout);
        write(STDOUT_FILENO, buf, ret);
    }

    if (fds[1].revents & POLL_IN)
    {
        struct signalfd_siginfo fdsi;
        read(sigfd, &fdsi, sizeof(fdsi));

        for (;;) {
            int status;
            pid_t pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
            if (pid == -1) {
                if (errno != ECHILD) {
                    perror("waitpid");
                }
                break;
            }
            if (pid == 0) {
                break;
            }

            printf("Child %u ", pid);
            if (WIFEXITED(status)) {
                printf("exited with status %i\n", WEXITSTATUS(status));
            } else if (WIFSIGNALED(status)) {
                printf("terminated by signal %i\n", WTERMSIG(status));
            } else if (WIFSTOPPED(status)) {
                printf("stopped by signal %i\n", WSTOPSIG(status));
            } else if (WIFCONTINUED(status)) {
                printf("continued\n");
            } else {
                printf("status unknown\n");
            }
        }
    }
}

close(sigfd);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM