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
| Name | What it does |
|---|---|
| fd = -1 | Create a new signalfd; pass an existing signalfd instead to replace its mask. |
| mask | Signals to accept. Block them with sigprocmask(2) first. SIGKILL and SIGSTOP are silently ignored. |
| SFD_NONBLOCKLinux 2.6.27 | Set O_NONBLOCK on the new descriptor. |
| SFD_CLOEXECLinux 2.6.27 | Set close-on-exec on the new descriptor. |
| struct signalfd_siginfo | What read(2) returns per signal: ssi_signo, ssi_pid, ssi_uid, ssi_code and more. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| EINVAL | fd is not a valid signalfd, or flags is invalid. |
| EBADF | fd is not a valid file descriptor. |
| EMFILE | Per-process open file descriptor limit reached. |
| ENOMEM | Insufficient memory to create a new signalfd. |
| ENODEV | Could 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
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
