Master the fundamental concepts of inter-process communication through this focused micro-challenge.
Signals are software interrupts delivered to processes: SIGINT from Ctrl-C, SIGCHLD when children exit, SIGSEGV on bad memory. Handlers run asynchronously unless masked, complicating reentrant code.
Safe patterns:
sigaction, not signalFor example, a SIGUSR1 handler setting got_signal = 1 lets the main thread drain work cooperatively.
SIGTERM-triggered graceful shutdown is exactly what systemd sends before SIGKILL when stopping a service, and Kubernetes relies on the same contract to give a pod's main process a chance to drain connections before its termination grace period expires. The volatile sig_atomic_t requirement here isn't pedantic: calling non-async-signal-safe functions like printf or malloc inside a real handler is a documented cause of heap corruption and deadlocks, since a signal can interrupt malloc mid-operation.
Before you call the implementation done, walk failure modes on purpose. Test empty structures, single-element edge cases, maximum concurrency, and errno paths that must not crash the program. OS code usually fails in production when happy-path tests pass but invariants break under contention or memory pressure.
Keep structures small and name fields after kernel counterparts when possible. That lets you read man pages and kernel source side by side while you work. Print observable events during development; remove noisy logs once tests pass reliably.
You will register handlers, send signals with kill, and reap children on SIGCHLD. This exercise requires demonstrating restart of slow syscalls with SA_RESTART vs EINTR.
Implement signal handling for process communication.
Requirements:
Test:
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
All starter code and reference implementations are available for your local setup.
View on Github