From 035027ad06149fa84fee0fdf3690eaba2a692be8 Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Fri, 22 Aug 2025 14:22:01 +0200 Subject: [PATCH] Example for chapter on UNIX signals --- README.md | 1 + chp10/README.md | 92 ++++++++++++++++++++++++++++++++++++++++++++ chp10/multihandler.c | 53 +++++++++++++++++++++++++ chp10/pause.c | 34 ++++++++++++++++ chp10/simple_catch.c | 20 ++++++++++ 5 files changed, 200 insertions(+) create mode 100644 chp10/README.md create mode 100644 chp10/multihandler.c create mode 100644 chp10/pause.c create mode 100644 chp10/simple_catch.c diff --git a/README.md b/README.md index 0d3bd55..192402f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Welcome! This is a collection of chapter summaries from the book **Linux System - [Chapter 6 - Advanced Process Management](chp6/README.md) - [Chapter 7 - Threading](chp7/README.md) - [Chapter 9 - Memory Management](chp9/README.md) +- [Chapter 10 - Signals](chp10/README.md) - [Exercises](exercises/README.md) > Each file contains an English summary of the chapter's key concepts. diff --git a/chp10/README.md b/chp10/README.md new file mode 100644 index 0000000..6fa1e11 --- /dev/null +++ b/chp10/README.md @@ -0,0 +1,92 @@ +# Chapter 10 – Signals (Summary) + +**Based on _Linux System Programming, 2nd Edition_ by Robert Love** +This chapter dives into the mechanics, pitfalls, and POSIX-compliant interfaces of signals in Linux. + +--- + +## What Are Signals? + +- **Signals** are asynchronous software interrupts used for interprocess communication (IPC). They notify processes of events such as: + - User-generated events (e.g., pressing Ctrl‑C sends **SIGINT**) + - Internal faults (e.g., divide by zero triggers **SIGFPE**) + - Kernel actions or process-related events (e.g., child termination sends **SIGCHLD**) +- Signals require special handling because they can occur at any moment, disrupting normal flow. + +--- + +## Signal Handling Models + +- Early Unix systems struggled with **unreliable signals**—deliveries could be missed or lost. +- **POSIX** standardized a **reliable signal model**, ensuring signals are not lost and behavior is predictable. + +--- + +## Setting Signal Handlers + +- Use the **`sigaction()`** interface (preferred) to set up handlers in a robust and portable way. +- `signal()` is outdated and has inconsistent behavior across platforms; use only for trivial cases like ignoring or using the default handler. +- **SIGKILL** and **SIGSTOP** cannot be caught, ignored, or blocked. + +--- + +## Signal Masks and Blocking + +- Each process has a **signal mask** (via `sigset_t`) that defines which signals are currently blocked. +- Signal blocking is essential when running critical code to avoid race conditions. +- Use `sigprocmask()` to modify the mask safely. + +--- + +## Common Signal APIs + +```c +int kill(pid_t pid, int signo); // Send a signal to another process +int killpg(int pgrp, int signo); // Send a signal to all processes in a given process group. Equivalent to kill(-pgrp, signo). +int raise(int signo); // Send a signal to self +unsigned int alarm(unsigned int seconds); // Schedule a SIGALRM +int pause(void); // Wait indefinitely until a signal arrives +void abort(void); // Raise SIGABRT and terminate +``` + +These calls allow fine-grained control over signal generation and handling. + +--- + +## Reentrancy and Safe Signal Handling + +- Signal handlers can interrupt your program at any point, even in the middle of non-reentrant functions, causing undefined behavior. +- Only call **async-signal-safe** functions within handlers (e.g., `write`, `_exit`). +- Avoid unsafe operations like `malloc()`, `printf()`, or `strtok()` inside signal handlers. + +--- + +## Process Behavior with Signals + +- Signal dispositions are inherited across `fork()` (child inherits parent’s handlers) but reset to default on `exec()`. + +--- + +## Summary of Key Concepts + +| Concept | Description | +|--------------------------|------------------------------------------------------------| +| Signals | Asynchronous notifications from kernel or processes | +| POSIX Signal Model | Reliable, standardized approach to signal delivery | +| `sigaction()` | Robust interface to set signal handlers | +| Signal Masks | Block/unblock signals to prevent interruptions | +| Async-signal-safe calls | Safe functions to call inside signal handlers | +| Core Functions | `kill`, `raise`, `alarm`, `pause`, `abort` | + +--- + +## Why This Matters + +Signals are indispensable for handling asynchronous events, timed operations, and interprocess interactions. Proper use of reliable signal handling, blocking, and safe coding practices ensures your applications remain responsive and stable under interruptions. + +--- + +## References + +- _Linux System Programming_ by Robert Love (2nd Ed.) — Chapter 10 +- POSIX and Linux signal handling docs: `signal()`, `sigaction()`, `sigprocmask()` diff --git a/chp10/multihandler.c b/chp10/multihandler.c new file mode 100644 index 0000000..8c48b99 --- /dev/null +++ b/chp10/multihandler.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +/* handler for SIGINT and SIGTERM */ +static void signal_handler(int signo) +{ + if (signo == SIGINT) + printf("Caught SIGINT!\n"); + else if (signo == SIGTERM) + printf("Caught SIGTERM!\n"); + else { + /* this should never happen */ + fprintf(stderr, "Unexpected signal!\n"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); +} + +int main(void) +{ + /* + * Register signal_handler as our signal handler + * for SIGINT. + */ + if (signal(SIGINT, signal_handler) == SIG_ERR) { + fprintf(stderr, "Cannot handle SIGINT!\n"); + exit(EXIT_FAILURE); + } + /* + * Register signal_handler as our signal handler + * for SIGTERM. + */ + if (signal(SIGTERM, signal_handler) == SIG_ERR) { + fprintf(stderr, "Cannot handle SIGTERM!\n"); + exit(EXIT_FAILURE); + } + /* Reset SIGPROF's behavior to the default. */ + if (signal(SIGPROF, SIG_DFL) == SIG_ERR) { + fprintf(stderr, "Cannot reset SIGPROF!\n"); + exit(EXIT_FAILURE); + } + /* Ignore SIGHUP. */ + if (signal(SIGHUP, SIG_IGN) == SIG_ERR) { + fprintf(stderr, "Cannot ignore SIGHUP!\n"); + exit(EXIT_FAILURE); + } + printf("Hi, I'm a process with PID %d\n", getpid()); + for (;;) + pause(); + return 0; +} diff --git a/chp10/pause.c b/chp10/pause.c new file mode 100644 index 0000000..2d6b705 --- /dev/null +++ b/chp10/pause.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +/* Handler for SIGINT */ +static void sigint_handler(int signo) +{ + /* + * Technically, you shouldn't use printf(...) in a + * signal handler, but it isn't the end of the + * world. I'll discuss why in the section + * "Reentrancy". + */ + printf("Caught SIGINT\n"); + exit(EXIT_SUCCESS); +} + +int main(void) +{ + /* + * Register sigint_handler as our signal handler + * for SIGINT. + */ + if (signal(SIGINT, sigint_handler) == SIG_ERR) { + fprintf(stderr, "Cannot handle SIGINT\n"); + exit(EXIT_FAILURE); + } + + for (;;) { + pause(); + } + return EXIT_SUCCESS; +} diff --git a/chp10/simple_catch.c b/chp10/simple_catch.c new file mode 100644 index 0000000..e3db1c6 --- /dev/null +++ b/chp10/simple_catch.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +void catch_sighup(int); + +int main(void) +{ + signal(SIGHUP, catch_sighup); + printf("Hi, I'm a process with PID %d\n", getpid()); + sleep(1000); + return EXIT_SUCCESS; +} + +void catch_sighup(int sig) +{ + printf("Catch signal SIGHUP\n"); + exit(EXIT_SUCCESS); +}