Linux - Linux Signals and Inter-Process Process Control

Linux signals are a fundamental mechanism used by the operating system to notify processes that a particular event has occurred. They allow processes to communicate with the kernel and with other processes without requiring them to continuously check for changes. Signals are commonly used to terminate processes, pause and resume execution, handle errors, reload configurations, and coordinate activities between processes.

1. What Is a Signal in Linux?

A signal is an asynchronous notification delivered to a process or thread. When a process receives a signal, Linux can take a predefined action, or the program can provide its own signal-handling function.

For example, suppose a program is running continuously in the terminal. Pressing Ctrl+C normally sends the SIGINT signal to that process. The default behavior of SIGINT is to terminate the process.

Signals are identified by names and numbers. Some commonly used signals include:

Signal Number Purpose
SIGHUP 1 Hangup or request configuration reload
SIGINT 2 Interrupt from the keyboard
SIGQUIT 3 Quit and produce a core dump
SIGKILL 9 Immediately terminate a process
SIGTERM 15 Request graceful termination
SIGSTOP 19 Stop or pause a process
SIGCONT 18 Continue a stopped process
SIGCHLD 17 Notify a parent when a child process changes state
SIGUSR1 10 User-defined signal
SIGUSR2 12 User-defined signal

The exact numbering can vary on some architectures, so signal names are generally preferred in scripts and programs.

2. How Signals Work

A simplified signal lifecycle is:

Event occurs → Signal is generated → Signal is delivered → Process handles the signal

For example:

User presses Ctrl+C
        |
        v
SIGINT is generated
        |
        v
Signal is delivered to the foreground process
        |
        v
Process performs its SIGINT action
        |
        v
Process terminates or handles the interruption

A signal can be generated by several sources. The kernel can generate signals when certain events occur. One process can send a signal to another process, and a user can indirectly generate signals through terminal operations.

For example:

kill -TERM 2456

This sends SIGTERM to process ID 2456.

Despite its name, the kill command does not necessarily mean "forcefully terminate." It is a general command for sending signals to processes.

3. SIGTERM: Graceful Process Termination

SIGTERM is one of the most important signals for process management.

When a process receives SIGTERM, it is being asked to terminate. The application has an opportunity to perform cleanup operations before exiting.

For example:

kill -TERM 2456

A well-designed application might respond by:

  1. Stopping new work.

  2. Finishing currently running operations.

  3. Closing files.

  4. Closing network connections.

  5. Saving necessary information.

  6. Releasing resources.

  7. Exiting normally.

This is why SIGTERM is normally preferred over SIGKILL when stopping an application.

4. SIGKILL: Immediate Termination

SIGKILL forcibly terminates a process:

kill -KILL 2456

or:

kill -9 2456

Unlike SIGTERM, a process cannot catch, ignore, or handle SIGKILL.

This makes SIGKILL powerful but potentially disruptive. The application does not get an opportunity to perform its normal cleanup.

For example, if an application is writing data when it receives SIGKILL, it may not get an opportunity to complete its operation cleanly.

A good general practice is:

SIGTERM → wait → SIGKILL if necessary

rather than immediately using kill -9.

5. SIGINT and Ctrl+C

When a user presses:

Ctrl+C

the terminal normally sends SIGINT to the foreground process group.

For example:

ping example.com

If the command is running continuously, pressing Ctrl+C interrupts it.

Applications can handle SIGINT differently depending on their design. A command-line application might terminate immediately, while another program might use the signal to initiate a controlled shutdown.

6. SIGSTOP and SIGCONT

Linux provides signals for stopping and continuing processes.

SIGSTOP suspends a process:

kill -STOP 2456

The process remains in memory but stops executing.

To resume it:

kill -CONT 2456

SIGCONT tells the stopped process to continue execution.

These signals are useful for temporarily suspending a process without terminating it.

For example:

Running
   |
   | SIGSTOP
   v
Stopped
   |
   | SIGCONT
   v
Running

SIGSTOP, like SIGKILL, cannot be caught or ignored by the target process.

7. SIGHUP and Configuration Reloading

Historically, SIGHUP represented a terminal hangup. Today, many Linux services use it for another purpose: requesting that a service reload its configuration.

For example:

kill -HUP 2456

However, the exact behavior depends on the application. Not every program treats SIGHUP as a configuration-reload request.

This is important when administering servers. The correct signal should always be verified from the application's documentation.

8. SIGCHLD and Parent-Child Processes

SIGCHLD is particularly important when working with parent and child processes.

When a child process terminates or changes state, the kernel can notify its parent using SIGCHLD.

Consider:

Parent Process
      |
      | creates
      v
Child Process
      |
      | finishes
      v
SIGCHLD sent to parent

The parent can then collect information about the child process.

This mechanism is closely related to the concept of process reaping. A parent normally uses system calls such as wait() or waitpid() to collect the child's termination status.

If a parent fails to properly reap terminated children, zombie processes can accumulate.

9. Sending Signals with the kill Command

The basic syntax is:

kill [signal] PID

For example:

kill -TERM 2456

Another form is:

kill -15 2456

To send SIGKILL:

kill -9 2456

To send SIGSTOP:

kill -STOP 2456

To send SIGCONT:

kill -CONT 2456

You can see available signals with:

kill -l

This displays the signals supported by the system.

10. Finding a Process ID

Before sending a signal, you often need the process ID.

You can use:

ps aux

For a particular process, tools such as:

pgrep nginx

can be useful.

For example:

pgrep nginx

might return:

2456
2510
2534

You could then send a signal to one of those processes.

Another useful command is:

pidof nginx

which can return the process IDs associated with the specified program.

11. Signal Handling by Applications

A Linux application can define how it responds to many signals.

For example, a server might receive SIGTERM and execute a cleanup routine before exiting.

Conceptually:

SIGTERM received
       |
       v
Signal handler
       |
       +--> Stop accepting new connections
       |
       +--> Finish current operations
       |
       +--> Close resources
       |
       +--> Exit

Programming languages and libraries provide mechanisms for registering signal handlers.

In C, modern applications commonly use sigaction() rather than the older signal() interface because sigaction() provides more predictable and flexible behavior.

12. Signals That Cannot Be Caught

Most signals can be handled or ignored by an application, but there are important exceptions.

Two particularly important signals are:

SIGKILL
SIGSTOP

A program cannot install a handler for SIGKILL or SIGSTOP.

This design gives administrators and the operating system reliable mechanisms for terminating or stopping a process even if the process is malfunctioning.

For example, if a program is stuck and does not respond to SIGTERM, an administrator can eventually use:

kill -9 PID

13. Signal Masks

A process can temporarily block certain signals.

A signal mask specifies which signals are currently blocked from normal delivery.

Blocking does not necessarily mean the signal disappears. Depending on the signal type, a pending signal can remain until it becomes unblocked.

Conceptually:

Signal generated
       |
       v
Is signal blocked?
   /        \
 Yes         No
 |            |
 v            v
Pending     Delivered
 |
 v
Unblocked later
 |
 v
Delivered

Signal masks are particularly important in multithreaded applications because threads can have different signal masks.

14. Pending Signals

A signal is considered pending when it has been generated but has not yet been delivered to the process or thread.

This can happen when a signal is temporarily blocked.

For example:

SIGTERM generated
       |
       v
Signal blocked
       |
       v
Signal becomes pending
       |
       v
Signal is unblocked
       |
       v
Signal is delivered

Understanding pending signals is useful when troubleshooting applications that appear not to respond immediately to administrative commands.

15. Signals and Process Groups

Signals are not limited to individual processes.

Linux also supports process groups. This is especially important for terminal applications and job control.

For example, a shell may create a group containing several related processes:

Shell
 |
 +-- Process A
 |
 +-- Process B
 |
 +-- Process C

A signal can be directed toward a process group rather than only one process.

This allows related processes to be controlled together.

For example, terminal-generated signals such as SIGINT are normally delivered to the foreground process group.

16. Signals and Inter-Process Communication

Signals are one form of inter-process communication, commonly called IPC.

IPC allows separate processes to exchange information or coordinate actions.

Signals are particularly useful for notification rather than transferring large amounts of data.

For example:

Process A
   |
   | SIGUSR1
   v
Process B

Process A can notify Process B that an event occurred.

For more substantial data exchange, other IPC mechanisms are generally more appropriate, such as:

  • Pipes

  • UNIX domain sockets

  • Message queues

  • Shared memory

  • Semaphores

Therefore, signals are best viewed as lightweight event notifications rather than a general-purpose data-transfer mechanism.

17. User-Defined Signals

Linux provides:

SIGUSR1
SIGUSR2

for application-specific purposes.

A developer can assign their own meaning to these signals.

For example, an application might define:

SIGUSR1 → Reload application settings
SIGUSR2 → Produce diagnostic information

The meaning is determined by the application, not by Linux itself.

These signals can be sent using:

kill -USR1 PID

or:

kill -USR2 PID

18. The kill Command and Permissions

A user cannot necessarily send arbitrary signals to every process on the system.

Linux uses process ownership and permissions to control signal delivery. In general, users can signal processes they have appropriate permission to control, while privileged users can manage a much broader range of processes.

For example:

kill -TERM 2456

may fail if the current user does not have permission to signal process 2456.

An administrator may need appropriate privileges to control a system service owned by another user.

19. The pkill and killall Commands

When many processes share the same name, identifying individual PIDs can be inconvenient.

pkill allows signals to be sent based on process attributes.

For example:

pkill -TERM nginx

This can send SIGTERM to matching processes.

Another command commonly available on Linux systems is:

killall nginx

The exact behavior and matching rules should be checked on the particular Linux distribution because implementations can differ.

For administrative scripts, precise process selection is important to avoid accidentally signaling unrelated processes.

20. Practical Example: Graceful Shutdown

Suppose a web server has PID 2456.

First, request a graceful shutdown:

kill -TERM 2456

Then check whether it is still running:

ps -p 2456

If the application has stopped, no further action is needed.

If it remains stuck and there is a legitimate reason to force termination:

kill -KILL 2456

The process-management sequence is therefore:

Identify process
      |
      v
Send SIGTERM
      |
      v
Allow cleanup
      |
      v
Check process status
      |
      +---- Stopped ----> Done
      |
      +---- Still running
                    |
                    v
               SIGKILL if required

21. Signals in Shell Scripts

Signals are also important when writing shell scripts.

A script can use the trap command to specify actions when certain signals are received.

For example:

trap 'echo "Termination requested"; exit 0' TERM

This tells the shell to execute the specified action when it receives SIGTERM.

A more practical script might use trap to remove temporary files before exiting:

cleanup() {
    rm -f /tmp/myfile
}

trap cleanup EXIT

This allows cleanup logic to be centralized rather than duplicated throughout the script.

22. Signal Safety

Signal handling has important programming constraints.

A signal can arrive asynchronously, meaning it can interrupt a program while the program is performing another operation.

Therefore, signal handlers should generally perform only safe and limited operations.

For example, complex operations inside a signal handler can introduce race conditions or inconsistent program state.

For sophisticated applications, developers often use signals only to record that an event occurred and then perform the actual work in the application's normal execution flow.

23. Signals in Multithreaded Programs

Signals become more complicated when an application contains multiple threads.

A process may contain:

Process
 |
 +-- Thread 1
 |
 +-- Thread 2
 |
 +-- Thread 3

Some signals are directed toward a specific thread, while others are directed toward the process and may be delivered to an appropriate thread according to Linux's signal rules.

Applications therefore need to carefully design signal handling when using multiple threads.

Linux provides mechanisms such as pthread_sigmask() for managing signal masks in threaded programs.

24. Signals and Job Control

Signals are fundamental to shell job control.

When you execute a command in a terminal, the shell can control whether it runs in the foreground or background.

For example:

sleep 100

Pressing Ctrl+Z normally causes the terminal to send SIGTSTP, which suspends the foreground job.

You can then use:

fg

to bring the job back to the foreground, or:

bg

to continue the stopped job in the background.

This demonstrates how signals, process groups, terminals, and shells work together.

25. Important Differences Between Common Signals

The most important distinctions are:

Signal Main Purpose Can Application Handle It?
SIGINT Interrupt a process Yes
SIGTERM Request graceful termination Yes
SIGKILL Force termination No
SIGSTOP Force process to stop No
SIGCONT Resume stopped process Yes, subject to process state
SIGHUP Terminal hangup or application-defined action Yes
SIGUSR1 Application-defined notification Yes
SIGUSR2 Application-defined notification Yes
SIGCHLD Child-process state notification Yes
SIGTSTP Terminal-generated stop request Yes

26. Best Practices for Signal-Based Process Control

When administering Linux systems, several practices are important.

First, prefer SIGTERM for normal process termination:

kill -TERM PID

Second, use SIGKILL only when a process cannot be terminated appropriately through normal mechanisms.

Third, verify the target process before sending a signal, particularly when using commands such as pkill or killall.

Fourth, understand that the same signal can have application-specific behavior. SIGHUP, for example, does not universally mean "reload configuration."

Fifth, avoid assuming that a successful kill command means the process has already exited. The command primarily requests signal delivery; the process may take time to respond.

Finally, applications should be designed to handle termination signals gracefully whenever possible.

Conclusion

Linux signals provide a lightweight and powerful mechanism for controlling processes and enabling communication between processes. Signals such as SIGTERM, SIGINT, SIGKILL, SIGSTOP, SIGCONT, and SIGCHLD have important roles in process management, while SIGUSR1 and SIGUSR2 allow applications to implement their own notification mechanisms.

Understanding signals is particularly valuable for Linux administrators, developers, DevOps engineers, and anyone working with server applications. The key distinction to remember is that SIGTERM requests a process to terminate gracefully, whereas SIGKILL forces immediate termination. Signals also form an important part of Linux job control, parent-child process management, service administration, and inter-process communication.