Skip to content

signalfd

signalfd converts selected signals into file descriptor events. This allows signal handling to be performed inside the normal event loop instead of inside an asynchronous signal handler.

Why signalfd?

Traditional signal handlers run asynchronously and can only safely call a limited set of functions. This makes cleanup and shutdown logic difficult.

signalfd changes the model:

signal
blocked pending signal
signalfd readable
epoll_wait()
read(signalfd)
normal event-loop shutdown path

Required Steps

  1. Build a signal mask.
  2. Block those signals with sigprocmask().
  3. Create a signalfd with the same mask.
  4. Add the signalfd to epoll.
  5. Read struct signalfd_siginfo when it becomes readable.

Minimal Pattern

sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);

sigprocmask(SIG_BLOCK, &mask, NULL);

int sigfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);

When epoll reports sigfd as readable:

struct signalfd_siginfo si;
read(sigfd, &si, sizeof(si));

if (si.ssi_signo == SIGINT || si.ssi_signo == SIGTERM) {
    /* Exit the event loop and run normal cleanup. */
}

Common Pitfalls

Pitfall Result
Not blocking the signal first Default signal action may still occur
Forgetting to read signalfd Repeated epoll wakeups
Doing cleanup inside a signal handler Unsafe or hard-to-debug behavior
Mixing traditional handlers and signalfd carelessly Confusing signal ownership