# Signals and Nonlocal Jumps ## Shell Linux Process Hierachy: all is rooted from `init` or `systemd` (pid = 1). A shell is an application that runs programs on behalf of the user. * `sh` Original Unix shell * `csh` BSD Unix C Shell * `bash` GNU Bourne-Again Shell (default Linux shell) ## Signals All signal is a small message that notifies a process that an event has occured in the system. Signal is sent by kernel. Signal is identified by small integer ID (1-30). Only information in a signal is **its ID** and **the fact that it arrived**. | ID | name | Default Action | Corresponding Event | | --- | ------- | -------------- | ----------------------------------- | | 2 | SIGINT | terminate | user types `Ctrl+C` | | 9 | SIGKILL | terminate | kill program | | 11 | SIGSEGV | terminate | segmentation violation | | 14 | SIGALRM | terminate | timer expired | | 17 | SIGCHLD | ignore | child process stopped or terminated | Sending a signal is essentially **updating some state** in the context of the destination process. Kernel sends a signal for one of the following reasons: A destination process receives a signal when it is forced by the kernel and reacts some way to the signal. * Ignore * Terminate (with optional core-dump) * Catch * executing a user-level handler function called **signal handler**. ### Pending & Blocking A signal is **pending** if sent but not yet received. There can be at most one pending signal of any particular type because signals are **not queued**. Signal is managed by bitmask. So subsequent signals of same type is discarded. * sets `k` bit when delivered * clears `k` bit when received Blocking signals: a process can block the receipt of certain signals. It is not received until unblocked. It is also managed by bitmask. * can be set and cleared by using `sigprocmask` ### Receiving Suppose kernel is returning from an exception handler and is ready to pass control to process `p` Kernel computes `pnb = pending(p) & ~blocked(p)`. * if `pnb == 0`, no unblocked pending signals for `p`, so just return to `p` normally. * otherwise, choose least nonzero bit `k` in `pnb` and force process `p` to receive signal `k` * The receipt of the signal triggers action by `p` * Repeat for all nonzero `k` ### Default Action & Signal Handler Each signal type has a predefined default action: * terminates * stops until restarted by a SIGCONT signal * ignores ```c handler_t * signal(int signum, handler_t *handler); ``` * `SIG_IGN` ignore * `SIG_DFL` revert to default action * handler function pointer ### Blocking and Unblocking * Implicit blocking Kernel blocks any pending signals of type currently being handled. * Explicit blocking `sigprocmask()` Supporting functions: * `sigemptyset()` create empty set * `sigfillset()` add every signal number to set * `sigaddset()` add signal number to set * `sigdelset()` delete signal number from set ```c sigset_t mask, prev_mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmaksk(SIG_BLOCK, &mask, &prev_mask); // critical section sigprocmask(SIG_SETMASK, &prev_mask, NULL); ``` ### Safe Signal Handling Handlers are concurrent with main program and share global data structures. This can lead to troubles. Some guidlines help to avoid troubles: 0. Keep you handlers as simple as possible 1. Call only **async-signal-safe** functions in your handlers 2. Save and restore `errno` on entry and exit 3. Protect accesses to shared data structure by temporarily blocking all signals 4. Declare global variables as `volatile` 5. Declare global flags as `volatile sig_atomic_t` #### Async-Signal-Safety A function satisfying either two conditions(below) is **Async-Signal-Safety function**: * **reentrant**: not modifying global data (or static data), only use local data(stack frame). * **non-interruptible**: blocking singnals during modifying global data. POSIX guarantees that the 117 functions to be async-signal-safe, including: * `_exit`, `write`, `wait`, `waitpid`, `sleep`, `kill` **IMPORTANT**: `printf`, `malloc`, `sprintf`, `eixt` are **NOT** async-signal-safe. ### Correct Signal Handling Example for reaping multiple childs ```c void child_handler(int sig) { int olderrno = errno; pid_t pid; while ((pid = wait(NULL)) > 0) { ccount --; sio_puts("Handler reaped child "); sio_putl((long) pid); sio_puts(" \n"); } if (errno != ECHILD) { sio_error("wait error"); } errno = olderrno; } ``` ### Portable Signal Handling Different UNIX systems have different signal handling sematnics. So `sigaction` is introduced to provide a portable interface for signal handling. ```c handler_t * Signal(int signum, handler_t *handler) { struct sigaction action, old_action; action.sa_handler = handler; sigemptyset(&action.sa_mask); // block sigs of type being handled action.sa_flags = SA_RESTART; // restart syscalls if possible if (sigaction(signum, &action, &old_action) < 0) unix_error("Signal error"); return (old_action.sa_handler); } ``` ### Concurrent Flows to lead Races ```c void handler(int sig) { int olderrno = errno; pid_t pid; while ((pid = waitpid(-1, NULL, 0)) > 0) { deletejob(pid); } if (errno != ECHILD) sio_error("waitpid error"); errno = olderrno; } int main() { while (1) { if ((pid = Fork()) == 0) { execve("/bin/date", argv, NULL); } } addjob(pid); } ``` In above example, the ecf flow can be executed before the deletejob; thus it cannot be deleted. Therefore, we need to synchronize the two concurrent flows. ```c void handler(int sig) { int olderrno = errno; sigset_t mask_all, prev_all; pid_t pid; sigfillset(&mask_all); while ((pid = waitpid(-1, NULL, 0)) > 0) { sigprocmask(SIG_BLOCK, &mask_all, &prev_all); deletejob(pid); sigprocmask(SIG_SETMASK, &prev_all, NULL); } if (errno != ECHILD) sio_error("waitpid error"); errno = olderrno; } int main() { pid_t pid; sigset_t mask_all, prev_one; sigfillset(&mask_all); signal(SIGCHLD, handler); while (1) { sigprocmask(SIG_BLOCK, &mask_all, &prev_one); // Block SIGCHLD if ((pid = Fork()) == 0) { sigprocmask(SIG_SETMASK, &prev_one, NULL); // Unblock SIGCHLD execve("/bin/date", argv, NULL); } sigprocmask(SIG_BLOCK, &mask_one, NULL); addjob(pid); sigprocmask(SIG_SETMASK, &prev_one, NULL); } } ```