Skip to content

signal: implement signalfd(2) - #1421

Closed
gburd wants to merge 1 commit into
cloudius-systems:masterfrom
gburd:pr/signalfd
Closed

signal: implement signalfd(2)#1421
gburd wants to merge 1 commit into
cloudius-systems:masterfrom
gburd:pr/signalfd

Conversation

@gburd

@gburd gburd commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

What

signalfd(2) was stubbed to return ENOSYS, so event loops that consume
signals through a file descriptor (a common pattern with epoll) could not run.

How

Implemented as a pollable special_file (modeled on eventfd) backed by a
global registry of live signalfd objects. OSv is a single process, so a
signalfd watches a set of signals for the whole image. kill() now checks the
registry first: if any signalfd is watching the raised signal, the signal is
queued to those fds (as a signalfd_siginfo) and their pollers are woken, and
the signal is consumed -- the default action and any handler are skipped. This
matches Linux, where a signalfd-consumed signal must be blocked and is dequeued
through the fd.

  • signalfd(-1, mask, flags) creates a new fd; signalfd(fd, mask, flags)
    updates an existing one's mask. SFD_CLOEXEC and SFD_NONBLOCK are honored.
  • read() returns as many queued signalfd_siginfo structures as fit in the
    buffer, blocking (or EAGAIN when non-blocking) when the queue is empty, and
    rejecting a buffer smaller than one siginfo with EINVAL.
  • poll() reports POLLIN when signals are queued.

The delivery hook (osv_signalfd_deliver) is declared in <osv/signal.hh> and
called from kill() in libc/signal.cc; the stub there is removed.

Testing

tests/tst-signalfd.cc covers delivery of a watched signal to the fd (instead
of the default poweroff action), the EAGAIN/poll/queue-order paths, and the
EINVAL cases. Passes on OSv under KVM (1 and 2 vCPUs). tst-sigwait and
tst-sigaction continue to pass, so normal signal delivery is unaffected for
signals no signalfd is watching.

Note

OSv's signal model is thread-directed and minimal; this integrates at the
process-wide kill() delivery point, which is the right granularity for a
single-address-space unikernel. Signals raised synchronously in-thread (e.g.
faults) are a separate path and not routed to signalfd here.

signalfd() was stubbed to return ENOSYS, so event loops that consume signals
through a file descriptor (a common pattern with epoll) could not run.

Implement it as a pollable special_file (modeled on eventfd) backed by a global
registry of live signalfd objects.  OSv is a single process, so a signalfd
watches a set of signals for the whole image.  kill() now checks the registry
first: if any signalfd is watching the raised signal, the signal is queued to
those fds (as a signalfd_siginfo) and their pollers are woken, and the signal is
consumed -- the default action and any handler are skipped.  This matches Linux,
where a signalfd-consumed signal must be blocked and is dequeued through the fd.

- signalfd(-1, mask, flags) creates a new fd; signalfd(fd, mask, flags) updates
  an existing one's mask.  SFD_CLOEXEC and SFD_NONBLOCK are honored.
- read() returns as many queued signalfd_siginfo structures as fit in the
  buffer, blocking (or EAGAIN when non-blocking) when the queue is empty, and
  rejecting a buffer smaller than one siginfo with EINVAL.
- poll() reports POLLIN when signals are queued.

The delivery hook (osv_signalfd_deliver) is declared in <osv/signal.hh> and
called from kill() in libc/signal.cc; the stub there is removed.

Add tests/tst-signalfd.cc covering delivery of a watched signal to the fd
(instead of the default poweroff action), the EAGAIN/poll/queue-order paths,
and the EINVAL cases.  Passes on OSv under KVM (1 and 2 vCPUs); tst-sigwait and
tst-sigaction continue to pass (normal signal delivery is unaffected for signals
no signalfd is watching).
@gburd gburd closed this Jul 13, 2026
@gburd
gburd deleted the pr/signalfd branch July 13, 2026 11:19
@gburd

gburd commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1439 (rebased onto current master; GitHub auto-closed this one when the branch was updated). Please review #1439 instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant