SIGINT And Other Termination Signals in Linux(转载)
原文地址:https://www.baeldung.com/linux/sigint-and-other-termination-signals
Overview
In Linux systems, processes can receive a variety of signals, such as SIGINT or SIGKILL. Each signal is sent in different situations and each has different behavior.
In this article, we’ll talk about SIGINT, SIGTERM, SIGQUIT, and SIGKILL. We’ll also see the difference between them.
Introduction to Signals
The signals are a method of communication between processes. When a process receives a signal, the process interrupts its execution and a signal handler is executed.
How the program behaves usually depends on the type of signal received. After handling the signal, the process may or may not continue its normal execution.
The Linux kernel can send signals, for instance, when a process attempts to divide by zero it receives the SIGFPE signal.
We can also send signals using the kill program. Let’s run a simple script in the background and stop it:
1 | (sleep 30; echo "Ready!") & |
Now, we can resume it using SIGCONT:
1 | kill -SIGCONT 26929 |
Alternatively, we can send signals in a terminal using key combinations. For instance, Ctrl+C sends SIGINT, Ctrl+S sends SIGSTOP, and Ctrl+Q sends SIGCONT.
Each signal has a default action, but a process can override the default action and handle it differently, or ignore it. However, some signals can’t be ignored nor handled differently and the default action is always executed.
We can handle signals in bash using the trap
command. For instance, we can add trap date SIGINT
in a script and it will print the date when SIGINT is received.
SIGINT
SIGINT is the signal sent when we press Ctrl+C. The default action is to terminate the process. However, some programs override this action and handle it differently.
One common example is the bash interpreter. When we press Ctrl+C it doesn’t quit, instead, it prints a new and empty prompt line. Another example is when we use gdb to debug a program. We can send SIGINT with Ctrl+C to stop the execution and return it to the gdb’s interpreter.
We can think of SIGINT as an interruption request sent by the user. How it is handled usually depends on the process and the situation.
Let’s write handle_sigint.sh using the trap command to handle SIGINT and print the current date:
1 |
|
We use read input to wait for the user interaction. Now, let’s run our script and let’s press Ctrl+C:
1 | ./handle_sigint.sh |
We can see the script didn’t exit. We can now terminate the script by writing some input:
1 | ./handle_sigint.sh |
If we want to use a signal to terminate it, we can’t use SIGINT with this script. We should use SIGTERM, SIGQUIT, or SIGKILL instead.
SIGTERM and SIGQUIT
The SIGTERM and SIGQUIT signals are meant to terminate the process. In this case, we are specifically requesting to finish it. SIGTERM is the default signal when we use the kill command.
The default action of both signals is to terminate the process. However, SIGQUIT also generates a core dump before exiting.
When we send SIGTERM, the process sometimes executes a clean-up routine before exiting.
We can also handle SIGTERM to ask for confirmation before exiting. Let’s write a script called handle_sigterm.sh to terminate only If the user sends the signal twice:
1 |
|
Now, let’s run it on background executing $ ./handle_sigterm.sh &
. Then, we run $ kill <PID>
twice:
1 | ./handle_sigterm.sh & |
As we can see, the script exited after it received the second SIGTERM.
SIGKILL
When a process receives SIGKILL it is terminated. This is a special signal as it can’t be ignored and we can’t change its behavior.
We use this signal to forcefully terminate the process. We should be careful as the process won’t be able to execute any clean-up routine.
One common way of using SIGKILL is to first send SIGTERM. We give the process some time to terminate, we may also send SIGTERM a couple of times. If the process doesn’t finish on its own, then we send SIGKILL to terminate it.
Let’s rewrite the previous example to try to handle SIGKILL and ask for confirmation:
1 |
|
Now, let’s run it on a terminal, and let’s send SIGKILL only once with $ kill -SIGKILL <pid>
:
1 | ./handle_sigkill.sh |
We can see it terminate right away without asking to re-send the signal.
How SIGINT Relates to SIGTERM, SIGQUIT and SIGKILL
Now that we understand more about signals, we can see how they relate to each other.
The default action for SIGINT, SIGTERM, SIGQUIT, and SIGKILL is to terminate the process. However, SIGTERM, SIGQUIT, and SIGKILL are defined as signals to terminate the process, but SIGINT is defined as an interruption requested by the user.
In particular, if we send SIGINT (or press Ctrl+C) depending on the process and the situation it can behave differently. So, we shouldn’t depend solely on SIGINT to finish a process.
As SIGINT is intended as a signal sent by the user, usually the processes communicate with each other using other signals. For instance, a parent process usually sends SIGTERM to its children to terminate them, even if SIGINT has the same effect.
In the case of SIGQUIT, it generates a core dump which is useful for debugging.
Now that we have this in mind, we can see we should choose SIGTERM on top of SIGKILL to terminate a process. SIGTERM is the preferred way as the process has the chance to terminate gracefully.
As a process can override the default action for SIGINT, SIGTERM, and SIGQUIT, it can be the case that neither of them finishes the process. Also, if the process is hung it may not respond to any of those signals. In that case, we have SIGKILL as the last resort to terminate the process.
Conclusion
In this article, we learned about signals and the difference between SIGINT, SIGTERM, SIGQUIT, and SIGKILL. Also, we briefly learned how to handle signals in bash.
We saw how SIGINT sometimes doesn’t kill the process as it may a different meaning. On the other hand, the SIGKILL signal will always terminate the process.
We also learned that SIGQUIT generates a core dump by default and that SIGTERM is the preferred way to kill a process.