# Linux Signals

## Summary


A Linux computer system has many processes in different states. These processes belong to either user applications or the operating system. We need a mechanism for the kernel and these processes to coordinate their activities. One way to do this is for a process to inform others when something important happens. This is why we have **signals**. 

A signal is basically a one-way notification. A signal can be sent by the kernel to a process, by a process to another process, or a process to itself. 

Linux signals trace their origins to Unix signals. In later Linux versions, real-time signals were added. Signals are a simple and lightweight form of interprocess communication, and therefore suited for embedded systems.

## Discussion

### What are the essential facts about Linux signals?

There are 31 standard signals, numbered 1-31. Each signal is named as "SIG" followed by a suffix. Starting from version 2.2, the Linux kernel supports 33 different real-time signals. These have numbers 32-64 but programmers should instead use `SIGRTMIN+n` notation. Standard signals have specific purposes but the use of SIGUSR1 and SIGUSR2 can be defined by applications. Real-time signals are also defined by applications. 

Signal number 0, which POSIX.1 calls *null signal*, is generally not used but `kill` function uses this as a special case. No signal is sent but it can be used (rather unreliably) to check if the process still exists. 

Linux implementation of signals is fully POSIX compliant. Newer implementation should prefer to use `sigaction` rather than the traditional `signal` interface. 

Just as hardware subsystems can interrupt the processor, signals interrupt process execution. They are therefore seen as *software interrupts*. While interrupt handlers process hardware interrupts, signal handlers process signals. 

Some signals are mapped to specific key inputs: SIGINT for `ctrl+c`, SIGSTOP for `ctrl+z`, SIGQUIT for `ctrl+\`. 


### How does a signal affect the state of a Linux process?

Some signals terminate the receiving process: SIGHUP, SIGINT, SIGTERM, SIGKILL. There are signals that terminate the process along with a core dump to help programmers debug what went wrong: SIGABRT (abort signal), SIGBUS (bus error), SIGILL (illegal instruction), SIGSEGV (invalid memory reference), SIGSYS (bad system call). Other signals stop the process: SIGSTOP, SIGTSTP. SIGCONT is a signal that resumes a stopped process.  

A program could override default behaviour. For example, an interactive program could be written to ignore SIGINT (generated by `ctrl+c` input). Two notable exceptions are SIGKILL and SIGSTOP signals, which can't be ignored, blocked or overridden this way. 

Let's consider the example of a parent process and its child process. Suppose the child sends SIGSTOP to itself, child process will be stopped. This in turns triggers SIGCHLD to the parent. Parent can then signal the child to continue using SIGCONT. When child comes out of stopped state, another SIGCHLD is sent to parent. If later, the child process exits, the final SIGCHLD is sent to parent. 


### Aren't signals similar to exceptions?

Some programming languages are capable of exceptions using constructs such as `try-throw-catch`. Signals are not similar to exceptions. Instead, failed system or library calls return non-zero exit codes. When a process is terminated, it's exit code will be 128 plus signal number. For example, a process killed by SIGKILL will return 137 (128+9). 


### Are Linux signals synchronous or asynchronous?

When signals are generated, they can be considered as synchronous or asynchronous.

**Synchronous** signals occur as a result of instructions that have led to an unrecoverable error such as an illegal address access. These signals are sent to the thread that caused it. These are also called *traps*, since they also cause a trap into the kernel trap handler. 

**Asynchronous** signals are external to the current execution context. Sending SIGKILL from an another process is an example of this. These are also called *software interrupts*. 


### What's the typical lifecycle of a signal?

A signal goes through three stages:

    + **Generation**: A signal can be generated by the kernel or any of the processes. Whoever generates the signal, addresses it to a specific process. A signal is represented by its number and has no extra data or arguments. Thus, signals are lightweight. However, extra data can be passed along for POSIX real-time signals. System calls and functions that can generate signals include `raise`, `kill`, `killpg`, `pthread_kill`, `tgkill`, and `sigqueue`.
    + **Delivery**: A signal is said to be *pending* until it's delivered. Normally a signal is delivered to a process as soon as possible by the kernel. However, if the process has blocked the signal, it will remain pending until unblocked.
    + **Processing**: Once a signal is delivered, it is processed in one of many ways. Every signal has an associated default action: ignore the signal; or terminate the process, sometimes with a core dump; or stop/continue the process. For non-default behaviour, *handler function* can be called. Exactly which of these happens is specified via `sigaction` function.

### Could you explain the concept of blocking and unblocking signals?

Signals interrupt the normal flow of program execution. This is undesirable when the process is executing some critical code or updating data that's shared with signal handlers. Blocking solves this problem. The tradeoff is that signal handling is delayed. 

Every process can specify if it wants to block a specific signal. If blocked and the signal does occur, the operating system will hold the signal as pending. The signal will be delivered once the process unblocks it. The set of currently blocked signals is called **signal mask**. 

There's no point blocking a signal indefinitely. For this purpose, the process can instead ignore the signal once it's delivered. 

A signal blocked by one processes doesn't affect other processes, who can receive the signal normally.

Signal mask can be set using `sigprocmask` (single-threaded) or `pthread_sigmask` (multi-threaded).  When a process has multiple threads, a signal can be blocked on a per thread basis. Signal will be delivered to any one thread that hasn't blocked it. Essentially, 

> Signal handlers are per process, signal masks are per thread.


### Can we have more than one signal pending for a process?

Yes, many standard signals can be pending for a process. However, only one instance of a given signal type can be pending. This is because pending and blocking of signals are implemented as bitmasks, with one bit per signal type. For example, we can have SIGALRM and SIGTERM pending at the same time but we can't have two SIGALRM signals pending. The process will receive only one SIGALRM signal even if raised multiple times. 

With real-time signals, signals can be queued along with data,  so that each instance of the signal can individually delivered and handled. 

POSIX doesn't specify the order of delivery for standard signals, or what happens if both standard and real-time signals are pending. Linux gives priority to standard signals. For real-time signals, lower numbered signals are delivered first, and if many are queued for a signal type, earliest one is delivered first. 

## Milestones

1990

Signals are described in the POSIX.1-1990 standard. These can be traced to the IEEE Std 1003.1-1988. 

1993

Real-time extensions are released as POSIX.1b. This includes real-time signals. 

1999

Linux starts supporting real-time signals with the release of kernel version 2.2.  

2001

More signals are added in the POSIX.1-2001 standard: SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ.