signalfd.2: Rewrite the text on epoll semantics

I also verified the behavior reported by Andrew Clayton
with the program below.

$ ./epoll_signalfd
PID of parent: 5661
PID of child:  5662
epoll_wait() returned 0
PID 5662: got signal 10
Successfully read signal, even though epoll_wait() didn't say FD was ready!

8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----
/* epoll_signalfd.c */

#include <sys/signalfd.h>
#include <signal.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

static void
signalTest(int sfd, int epfd)
{
    struct signalfd_siginfo fdsi;
    struct epoll_event rev;
    int ready;
    ssize_t s;

    usleep(50000);
    ready = epoll_wait(epfd, &rev, 1, 0);
    if (ready == -1)
        errExit("epoll_wait");

    printf("epoll_wait() returned %d\n", ready);

    s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
    if (s != sizeof(struct signalfd_siginfo))
        errExit("read");

    printf("PID %ld: got signal %d\n", (long) getpid(), fdsi.ssi_signo);

    if (ready == 0 && s > 0)
        printf("Successfully read signal, even though epoll_wait() "
                "didn't say FD was ready!\n");
}

int
main(int argc, char *argv[])
{
    struct epoll_event ev;
    sigset_t mask;
    int sfd, epfd;

    sigfillset(&mask);
    sigdelset(&mask, SIGINT);

    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
        errExit("sigprocmask");

    sfd = signalfd(-1, &mask, SFD_NONBLOCK);
    if (sfd == -1)
        errExit("signalfd");

    epfd = epoll_create(5);
    if (epfd == -1)
        errExit("epoll_create");

    ev.data.fd = sfd;
    ev.events = EPOLLIN;
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev) == -1)
        errExit("epoll_ctl");

    switch (fork()) {
    case -1:
        errExit("fork");
    case 0:
        printf("PID of child:  %ld\n", (long) getpid());
        raise(SIGUSR1);
        signalTest(sfd, epfd);
        break;
    default:
        printf("PID of parent: %ld\n", (long) getpid());
        wait(NULL);
        break;
    }

    exit(EXIT_SUCCESS);
}
8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----8x----

Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
This commit is contained in:
Michael Kerrisk 2019-09-23 16:36:45 +02:00
parent e95f6bf482
commit b892d64f4f
1 changed files with 26 additions and 16 deletions

View File

@ -261,23 +261,33 @@ itself and the signals that are directed to the process
(i.e., the entire thread group).
(A thread will not be able to read signals that are directed
to other threads in the process.)
.\"
.SS epoll(7) semantics
If you add a signalfd file descriptor to
.BR epoll(7)
then
.BR epoll_wait(2)
will only return events for signals received by the process that did
the
.BR epoll_ctl(2).
If you then
.BR fork(2),
say by calling
.BR daemon(3),
then you will find that you don't get any notifications for sent
signals. For this to work, you need to add the signalfd file
descriptor to
.BR epoll(7)
after forking.
If a process adds (via
.BR epoll_ctl (2))
a signalfd file descriptor to an
.BR epoll (7)
instance, then
.BR epoll_wait (2)
returns events only for signals sent to that process.
In particular, if the process then uses
.BR fork ()
to create a child process, then the child will be able to
.BR read (2)
signals that are sent to it using the signalfd file descriptor, but
.BR epoll_wait (2)
will
.B not
indicate that the signalfd file descriptor is ready.
In this scenario, a possible workaround is that after the
.BR fork (2),
the child process can close the signalfd file descriptor that it inherited
from the parent process and then create another signalfd file descriptor
and add it to the epoll instance.
Alternatively, the parent and the child could delay creating their
(separate) signalfd file descriptors and adding them to the
epoll instance until after the call to
.BR fork (2).
.SH RETURN VALUE
On success,
.BR signalfd ()