On this page
User-Space Signal Handling
Coverage: sigaction → sigframe (user stack) → SA_RESTART → signal stack (sigaltstack) → signalfd → timerfd → real-time signals Applicable to: glibc + Linux, user-space perspective (see kernel docs for kernel part)
Overview
Signals are the oldest yet still actively used IPC/notification mechanism in Unix. The key issue in user-space programming is not "how to send" (the kill system call), but "when signals are delivered, what the stack looks like when the handler executes, and where the program continues after the handler returns." This article focuses on the execution model of user-space signal handlers.
sigaction: Installing a Signal Handler
struct sigaction sa = ;
;
;
// Key bits in sa_flags:
// SA_SIGINFO: Use 3-argument handler (obtains siginfo + ucontext)
// SA_RESTART: Interrupted syscalls are automatically retried
// SA_NODEFER: Do not automatically block this signal during handler execution
// SA_RESETHAND: One-shot handler (restores SIG_DFL after execution)
// SA_ONSTACK: Use sigaltstack (independent signal stack)
Signal Frame: The "Ghost Stack" of Signal Handlers
// When a signal is delivered, the kernel creates a sigframe on the user stack:
//
// Normal stack (grows downward):
// [user data] ← original RSP
// [sigframe] ← "snapshot" constructed by the kernel
// ├─ Saved registers: RIP, RSP, RFLAGS, general-purpose regs
// ├─ siginfo_t (si_signo, si_code, si_addr, ...)
// └─ FPU state (XSAVE)
// ← new RSP (signal handler starts from here)
// Signal handler return:
// sigreturn() → restore all registers from sigframe → program continues
Key: Signal handlers run on the user stack (not the kernel stack) — they are ordinary user-space functions. The kernel simply saves the entire register context before calling it, and restores it after the handler returns.
Signals and System Calls
// System calls are interrupted by signals during "slow" operations (read from pipe/socket):
ssize_t ret;
do while ; // Manual retry
// Or: SA_RESTART → kernel automatically retries (but not all syscalls are restartable)
// Restartable: read/write/wait/pause/select/poll/...
// Non-restartable: sleep (returns remaining time), connect (returns error), accept
// pselect / ppoll / epoll_pwait:
// Atomic operation: unblock signal → wait → block signal
// Avoids race condition: signal arriving between unblock and wait
sigaltstack: Independent Signal Stack
// Problem: Stack overflow → SIGSEGV → signal handler needs to run on the stack → but the stack is full!
// Solution: Allocate an independent stack for signal handlers (sigaltstack)
stack_t ss = ;
;
struct sigaction sa = ;
;
// Use cases:
// - SIGSEGV handler (safely record crash information)
// - Go runtime: goroutine preemption (SIGURG + independent signal stack)
// - Profiling: SIGPROF handler
signalfd: Signals as File Descriptors
// Traditional signal problem: Asynchronous → handlers can only call async-signal-safe functions
// signalfd: Treat signals as readable fds (synchronous) → can be used with poll/epoll
sigset_t mask;
;
;
;
; // Block first (otherwise default behavior applies)
int sfd = ;
// Event loop:
struct signalfd_siginfo fdsi;
;
;
// Works with epoll → all events handled uniformly
timerfd: Timers as File Descriptors
int tfd = ;
struct itimerspec its = ;
;
// Each expiration → fd becomes readable → read returns 1 (number of expirations)
uint64_t expirations;
;
Real-Time Signals (SIGRTMIN ~ SIGRTMAX)
POSIX real-time signals vs. traditional signals:
Traditional (SIGINT, SIGTERM, ...): Multiple sends → may be merged into a single delivery (queue limit = 1)
Real-time signals (SIGRTMIN+): Multiple sends → queued in order → no loss
Real-time signals can carry additional data:
union sigval value;
value.sival_int = 42;
sigqueue(target_pid, SIGRTMIN, value);
Debugging
# Process signal status
|
# SigBlk: blocked, SigIgn: ignored, SigCgt: caught (has handler)
# SigPnd: pending per-thread, ShdPnd: pending per-process
# Send signals
# Trace signals with strace
References
- man: sigaction(2), sigaltstack(2), signalfd(2), timerfd_create(2)
- Source code: glibc
signal/sigaction.c,sysdeps/unix/sysv/linux/signalfd.c - LWN: "Signalfd and timerfd", "POSIX real-time signals"
Keywords: sigaction, sigframe, SA_RESTART, sigaltstack, SA_ONSTACK, signalfd, timerfd, real-time signals, SIGEV_THREAD