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¶
- Build a signal mask.
- Block those signals with
sigprocmask(). - Create a signalfd with the same mask.
- Add the signalfd to epoll.
- Read
struct signalfd_siginfowhen 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 |