In most cases, kill is used as a last resort. It ends a process without mercy. Systems administrators sometimes kill a process with the kill -9 <PID> command – using the signal 9, which means kill even if the signal is not catchable or ignorable) and the process ID (PID) argument. You can get the PID by using the ps command.
The standard Linux signals are listed in Table 1. Related systems, such as FreeBSD, have their own variations, with the signals 1, 3, 9, and 15 being the only uniform ones throughout.
Table 1
Signals
1 | SIGHUP | Separates child from parent process | Ends process |
2 | SIGINT | Ends process | Same as Ctrl+C |
3 | SIGQUIT | Ends process | Process can create core dump |
4 | SIGILL | Ends process | After a false call (no privileges, unknown functions) |
5 | SIGTRAP | Ends process | Process triggers trace/debugger |
6 | SIGABRT | Ends process | From process itself |
7 | SIGBUS | Ends process | System call after memory access errors |
8 | SIGFPE | Ends process | Division by 0 |
9 | SIGKILL | Ends process | No data written, risk of data loss |
10 | SIGUSR1 | User-defined signal | – |
11 | SIGSEGV | Ends process | After memory access errors |
12 | SIGUSR2 | User-defined signal | – |
13 | SIGPIPE | Ends process | Pipe error |
14 | SIGALRM | Ends process | After timer expiration |
15 | SIGTERM | Ends process | Restores files |
16 | SIGSTKFLT | Ends process | After co-processor stack error |
17 | SIGCHLD | Ends child process | Triggered by parent |
18 | SIGCONT | Continues process | |
19 | SIGSTOP | Pauses process | – |
20 | SIGTSTP | Pauses process | Same as Ctrl+Z |
You can determine which of the two methods your system uses with kill -l . You can use the kill command in the format kill -<number> <PID> or kill -s <signal_name> <PID> . When using the signal name, you don't need the preceding SIG , so you can either do:
$ kill -KILL 29737
or:
$ kill -9 29737
and it will do exactly the same thing: kill the process instantly.
For some of the examples, the target for the signals will be daemon.sh that I created in Listing 1. The program shows its shell command, its own PID, and the parent process PID in a loop function.
Listing 1
daemon.sh
#! /bin/sh while true; do echo $0 $$ $PPID sleep 1 done
The basic process-ending method is to use SIGHUP , where the system proceeds as if you had quit the terminal. Conversely, you can prevent this by using nohup at program startup. The application continues to run after you log off, such as with an SSH session.
As long as no one affects the process or shuts down the system, the script runs until it terminates by itself. You can read the output in the nohup.out protocol file by using tail -f nohup.out .
You can restart most system daemons in the same way: kill -HUP <daemon_PID> . It's still recommended, however, to use the daemon's init script or the system's start/stop mechanism instead.
If necessary, you can halt processes to allow other necessary resources to run. For example, the daemon.sh script gets a signal 19 while going through its motions, which pauses the process. You can continue the process using kill -18 <PID> (Figure 1).
There is really no way of capturing, blocking, or ignoring the SIGKILL and SIGSTOP signal with a trap. However, you can use all the other signals in shell scripts to elicit another response. You can do this using the trap '<response>' <signals> command in a shell script.
The trap '' 2 line ignores the signal. Without an option or a response, the command undefines almost all signals at the end of the shell script. Signal 10 essentially halts the script in Listing 2 with the output [2]+ User-defined signal 1 ./message.sh . The trap -l command lists the usable signals.
Listing 2
ignoresignals.sh
#! /bin/sh trap while true; do clear echo $0 $$ sleep 1 done
A trap opens up many possibilities. It allows some scripts to delete temporary files when unintentionally logging off the shell. You can also prevent stopping a script through pilot error by having it ignore signals. Conversely, you can use this technique to enter into a running script without needing to restart it.
Listing 3 (reaction.sh ) shows you how you can do this using a function. In the example, the script defines a variable and then displays it. Figure 2 shows the result.
Listing 3
reaction.sh
#! /bin/sh a="Default" # Define function signaltrap() { echo -n "Enter new value: ";read a if [ -z "$a" ]; then echo "No entry -> Abort!" exit fi continue exit 0 } # Trap signal 2 ([Crl]+[C]) trap 'signaltrap' 2 while true; do echo "$$: $a" sleep 3 done
Because of the continue after the loop, the script continues with the given variable value. Another shell sends the kill -2 signal. The terminal with the application accepts the input and the script continues. Another Ctrl+C and an empty input terminate the program.
You can find a program much prettier than the first attempt at a self-written script. For example, take a look at a retro clock, complete with sound output in the style of a C64 classic home computer (Figure 3). To make it work, you need two programs: figlet and beep .
Once you've installed the tools, the scripts in Listing 4 (clockmenu.sh ) and Listing 5 (clock.sh ) let you experiment further. To begin, start clock.sh , which creates behind a file named .clock.pid that contains the PID. Then, call clockmenu.sh , which extracts the number of the other process from the file. Now you can control the clock.
Listing 4
clockmenu.sh
#! /bin/sh # Get clock time process ID clockpid=$(cat .clock.pid) while true; do clear echo "Control of shell clock" echo " " echo "(l) Local time" echo "(u) UTC" echo "(w) Set alarm" echo "(e) Stop alarm" echo "(E) End control" echo " " echo -n "Select function: ";read f if [ "$f" = "E" ]; then exit elif [ "$f" = "e" ]; then kill -15 $clockpid elif [ "$f" = "w" ]; then echo -n "Enter wake-up time (null for delete): ";read wt echo "$wt" > .clock.wt kill -1 $clockpid elif [ "$f" = "l" ]; then kill -10 $clockpid elif [ "$f" = "u" ]; then kill -12 $clockpid fi done
Listing 5
clock.sh
#! /bin/bash # Ignore signals 2 and 20 trap '' 2 20 # Print process ID for controlling "clockmenu.sh" echo $$ > .clock.pid # Define empty variable for wake-up time $wt wt="" # Set time zone: local time lt="l" # Get wake-up time, or null if [ -e .clock.wt ]; then wt=$(cat .clock.wt) else touch .clock.wt fi # Main loop while true; do # Assign action to signals -- important: # for loop continuation, # don't forget continue! trap 'wt=$(cat .clock.wt); continue' 1 trap 'lt="l"; continue' 10 trap 'lt="u"; continue' 12 # Get times time=$(date +%H:%M:%S) utc=$(date -u +%H:%M:%S) shorttime=$(date +%H:%M) date=$(date +%A\ %d.%m.%Y) week=$(date +%V) hourchime=$(date +%I) quarterhourchime=$(date +%M) # Progess bar seconds: # For loop can't handle "08" or "09". # Therefore, seconds first stored in $a and for # $seconds and cast to computer bc for 0. a=$(date +%S) seconds=$(echo $a -0 | bc) clear # Display time if [ "$lt" = "l" ]; then figlet -f banner $time tzone="Local time" elif [ "$lt" = "u" ]; then figlet -h banner $utc tzone="UTC" fi # Display additional data echo " " echo "Set wake-up time: $wt" echo "$date : $week. Calendar week Timezone: $tzone" echo "----------------------------------------------------------------" # Second hand for((i=0; i<$seconds; i++)); do echo -n "#" done # Chime if [ "$quarterhourchime" != "$alarm" ]; then if [ $quarterhourchime -eq 15 ]; then beep -f 800 elif [ $quarterhourchime -eq 30 ]; then beep -f 800 -r 2 -l 500 -d 500 elif [ $quarterhourchime -eq 45 ]; then beep -f 800 -r 3 -l 500 -d 500 elif [ $quarterhourchime -eq 0 ]; then beep -f 800 -r 4 -l 500 -d 500 beep -f 700 -r $hourchime -l 500 -d 600 fi alarm=$quarterhourchime fi # Alarm if [ "$shorttime" = "$wt" ]; then if [ "$wt" != "$wakeupalarm" ]; then for (( i=1; i <= 5; i++ )); do beep -f 1000 -n -f 2000 -n -f 1500 -n -f 500 -n -f 300 -n -f 3000 done wakeupalarm=$wt fi fi sleep 1 done # Cleanup rm .clock.pid
Figure 4 shows the clock menu that, although rather Spartan, performs all the required functions. Setting the alarm time is done by entering it in the SS:MM format.
Thanks to the figlet tool, the clock appears in a large display. If you set an alarm, the script displays it. The alarm sound will turn off by itself, and the sound is unmistakable. With beep , you can conjure up different sounds from the computer.
The same is true for the hour chime. As with a classic clock, the quarter hour chimes are in a higher tone and the hour chimes are in a lower one. The hour chimes are divided into two 12-hour periods, and you can enter world time (UTC) or local time. Entering e in clockmenu.sh stops the clock.
Note that stopping and restarting the controlling software (i.e., clockmenu.sh ) is necessary after restarting the clock or the signals get sent to the wrong or non-existent process. The sounds that beep creates are generally emitted by the PC's speaker.
You can also use Htop to control the clock, as well as other processes shown in this article. In Figure 5, you can see how Htop is used to kill the reaction.sh script.
A Linux system provides a wealth of possibilities through its construct of processes. If you get more involved in the subject, you will soon discover that the robustness and long runtimes of Linux/UNIX computers largely rest upon this sophisticated method that makes perpetual restarts unnecessary.
For ambitious programmers, there are also fascinating opportunities to enhance a shell script with a user interface for communicating with the running program.