The signaling mechanism in the Linux kernel allows running applications to asynchronously notify the system when a new event occurs. Because of its nature, this signaling mechanism is generally known as software interrupts. Just like hardware interrupts, signals interrupt an application's normal flow, and it's unpredictable when an application will receive a signal.

Let's dive deep into the signaling mechanism in Linux and understand what goes on behind the scenes.

Basic Signal Concepts in Linux

On Linux, processes generate signals in three basic situations:

  • When an exceptional situation occurs on the hardware side. For example, you can think of events such as the application trying to access a region outside the allowed address space (segmentation fault) or generating machine code that includes a division by zero operation.
  • Situations such as the use of key combinations like Ctrl + C or Ctrl + Z on the console by the user, resizing the console screen, or sending a kill signal.
  • The timer set in the application expires, the CPU limit given to the application is high, the data comes to an open file descriptor, etc.

The concept of signals has been around since the early versions of Unix. Previously, there were several differences between Unix versions regarding signal processing. Later, with the POSIX standardization made for signal management, Linux and other Unix derivatives started following these standards. For this reason, the concepts of Unix signals and POSIX signals, which you may encounter in some documents, point to the differences.

Signal Numbers

Signals have various numerical values, starting with one. For example, signal 1 is a HUP signal in almost every system, or signal 9 is a KILL signal.

However, using these numbers is strongly discouraged when you use signals in your applications. For POSIX signals, signal.h file should be in the application and the developer should use the constant definitions of related numbers such as SIGHUP, SIGKILL, etc. instead.

If you examine the /usr/include/signal.h file on your system, you can see the additional operations and other included files by looking at the definitions of values such as __USE_POSIX, __USE_XOPEN, __USE_POSIX199309, etc. in the file. You can find the available signal numbers on Linux systems in the /usr/include/asm-generic/signal.h file, which you don't need to include directly in your application code.

Signal Generation and Send

Signal generation occurs due to an event. However, sending (delivering) the signal to the relevant application does not occur simultaneously with the generation of the signal.

For the signal to be sent to the application, the application must be currently running and have CPU resources. Therefore, the sending of a signal to a specific application occurs when the relevant application starts working again after the context switch.

The Pending Signal Concept

During the time from generation to transmission of the signal, the signals are in a standby state. You can access the number of pending signals and the number of pending signals allowed for a process from the /proc/PID/status file.

        # For a process with PID: 2299
cat /proc/2299/status


# Output
...
SigQ: 2/31630
...

Signal Masks and Blocking

The exact time the signals will arrive is often unpredictable by the application. Therefore, some critical interruptions may occur during any operation. This can cause major problems for a large-scale application.

To prevent some undesirable situations like this, it is necessary to use signal masks. Thus it is possible to block some signals before a critical operation. At this stage, it is important to complete the critical part and remove the defined blocks. This process is something that the application developer should pay attention to.

When the application blocks a signal, other signals of the same type generated will be in a waiting state until unblocked. In the application, the sending of pending signals is also provided as soon as the block is removed.

In this way, the same types of signals put on hold at the time of the block are sent to the application only once after the block is removed in normal use. The situation is different for real-time signals.

Linux Signal Types

Default actions may vary according to signal types. If the application that receives the corresponding signal does not have a signal handler function, the default action takes place. Sometimes this means terminating the application and sometimes ignoring the signal.

Some signals cannot be captured at the application layer, these signals always perform the default action (like the KILL signal).

In addition to some actions that cause an application to terminate, a core dump file is also produced. Core dump files, created by writing the virtual memory table of the related process to disk, help the user to examine the state information before the process ends with debugging tools in the next stages.

The following values are based on an exemplary MIPS architecture:

Signal

Number

Default Action

Can It Be Caught?

SIGHUP

1

Terminate application

Yes

SIGINT

2

Terminate application

Yes

SIGQUIT

3

Terminate application (core dump)

Yes

SIGILL

4

Terminate application (core dump)

Yes

SIGTRAP

5

Terminate application (core dump)

Yes

SIGABRT

6

Terminate application (core dump)

Yes

SIGFPE

8

Terminate application (core dump)

Yes

SIGKILL

9

Terminate application

No

SIGBUS

10

Terminate application (core dump)

Yes

SIGSEGV

11

Terminate application (core dump)

Yes

SIGSYS

12

Terminate application (core dump)

Yes

SIGPIPE

13

Terminate application

Yes

SIGALRM

14

Terminate application

Yes

SIGTERM

15

Terminate application

Yes

SIGUSR1

16

Terminate application

Yes

SIGUSR2

17

Terminate application

Yes

SIGCHLD

18

Ignore

Yes

SIGTSTP

20

Stop

Yes

SIGURG

21

Ignore

Yes

SIGPOLL

22

Terminate application

Yes

SIGSTOP

23

Stop

No

SIGCONT

25

Continue if stopped

Yes

SIGTTIN

26

Stop

Yes

SIGTTOU

27

Stop

Yes

SIGVTALRM

28

Terminate application

Yes

SIGPROF

29

Terminate application

Yes

SIGXCPU

30

Terminate application (core dump)

Yes

SIGXFSZ

31

Terminate application (core dump)

Yes

Life Cycle of Signals in Linux

Signals go through three stages. They are produced primarily in the production phase, by the kernel or any process, and are represented by a number. They work lightly and quickly, as they do not have any extra load on them. But if you look at the POSIX side, you'll see that real-time signals can transmit extra data.

The delivery phase for the signals comes after the production phase. Normally, signals reach the application from the kernel as quickly as possible. However, sometimes applications can block signals while doing critical operations. In such cases, the signal remains pending until the transaction takes place.

Like signals, processes are also an integral part of the Linux ecosystem. Understanding what processes are and how they work is crucial if you're planning to become a Linux system administrator.