Project 8: Signal Handler

Proper signal handling for an interactive shell: Ctrl+C (SIGINT) interrupts the foreground job but not the shell, Ctrl+Z (SIGTSTP) suspends the foreground job, and SIGCHLD lets you track child termination/stopping.

Quick Reference

Attribute Value
Primary Language C
Alternative Languages Rust, Zig
Difficulty Level 3: Advanced (The Engineer)
Time Estimate 1 week
Knowledge Area Operating Systems / Signals
Tooling Unix Shell
Prerequisites Projects 1-5, understanding of signals

What You Will Build

Proper signal handling for an interactive shell: Ctrl+C (SIGINT) interrupts the foreground job but not the shell, Ctrl+Z (SIGTSTP) suspends the foreground job, and SIGCHLD lets you track child termination/stopping.

Why It Matters

This project builds core skills that appear repeatedly in real-world systems and tooling.

Core Challenges

  • Ignoring signals in shell, not in children (sigaction before/after fork) → maps to signal inheritance
  • SIGCHLD handling (reaping zombies, detecting stopped jobs) → maps to async notification
  • Signal-safe functions (can’t call printf in signal handler) → maps to reentrancy
  • Interrupting system calls (EINTR handling) → maps to robust programming
  • Terminal signals and process groups (only foreground group gets SIGINT) → maps to job control foundation

Key Concepts

  • Signal handling: “The Linux Programming Interface” Chapters 20-22 - Kerrisk
  • Async-signal-safe functions: “Advanced Programming in the UNIX Environment” Chapter 10.6 - Stevens
  • Process groups and signals: “The GNU C Library Manual” Chapter 28 - GNU

Real-World Outcome

$ ./mysh
mysh> sleep 100
^C                          # Ctrl+C interrupts sleep
mysh>                       # But shell survives and prompts again
mysh> sleep 100
^Z                          # Ctrl+Z suspends sleep
[1]+ Stopped    sleep 100
mysh> sleep 200 &
[2] 12346
mysh>                       # Shell doesn't block
[2]  Done       sleep 200   # Background job completion reported
mysh>

Implementation Guide

  1. Reproduce the simplest happy-path scenario.
  2. Build the smallest working version of the core feature.
  3. Add input validation and error handling.
  4. Add instrumentation/logging to confirm behavior.
  5. Refactor into clean modules with tests.

Milestones

  • Milestone 1: Minimal working program that runs end-to-end.
  • Milestone 2: Correct outputs for typical inputs.
  • Milestone 3: Robust handling of edge cases.
  • Milestone 4: Clean structure and documented usage.

Validation Checklist

  • Output matches the real-world outcome example
  • Handles invalid inputs safely
  • Provides clear errors and exit codes
  • Repeatable results across runs

References

  • Main guide: SHELL_INTERNALS_DEEP_DIVE_PROJECTS.md
  • “The Linux Programming Interface” by Michael Kerrisk