$RodHat_
Syscall reference · Events

signalfd(2)

Accept signals by reading a file descriptor instead of running a handler.

Since
Linux 2.6.22check your kernel →
glibc
glibc 2.8
System
Linux
Man page
signalfd(2)

Synopsis

#include <sys/signalfd.h>

int signalfd(int fd, const sigset_t *mask, int flags);

Rod's take

The self-pipe trick is thirty years of a signal handler writing one byte so select() has something to wake up on. signalfd retires it. Block the signals with sigprocmask, create the fd on the same mask, and pending signals come out of read() as struct signalfd_siginfo, sender PID and UID included, which your old handler never had. No handler, no global flag, no race. The step people skip is the blocking: leave a signal unblocked and its default disposition gets there first. SIGKILL and SIGSTOP aren't invited, obviously.

Flags and arguments that matter

NameWhat it does
fd = -1Create a new signalfd; pass an existing signalfd instead to replace its mask.
maskSignals to accept. Block them with sigprocmask(2) first. SIGKILL and SIGSTOP are silently ignored.
SFD_NONBLOCKLinux 2.6.27Set O_NONBLOCK on the new descriptor.
SFD_CLOEXECLinux 2.6.27Set close-on-exec on the new descriptor.
struct signalfd_siginfoWhat read(2) returns per signal: ssi_signo, ssi_pid, ssi_uid, ssi_code and more.

Errors you will actually hit

errnoMeaning
EINVALfd is not a valid signalfd, or flags is invalid.
EBADFfd is not a valid file descriptor.
EMFILEPer-process open file descriptor limit reached.
ENOMEMInsufficient memory to create a new signalfd.
ENODEVCould not mount (internal) anonymous inode device.

Example

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
    err(1, "sigprocmask");

int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (sfd < 0)
    err(1, "signalfd");

struct signalfd_siginfo si;
if (read(sfd, &si, sizeof(si)) == sizeof(si))
    printf("signal %u from pid %u\n", si.ssi_signo, si.ssi_pid);

Trimmed from the full walkthrough in timerfd, signalfd, eventfd: every event source is a file descriptor now.

BSD equivalent

FreeBSD and OpenBSD: EVFILT_SIGNAL in kqueue(2).

The long version, from the console

Sources

  1. signalfd(2), man7.org
  2. kqueue(2), FreeBSD
  3. kqueue(2), OpenBSD

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.