One very common command line activity is process control. Linux, and other *nix operating systems come with a host of programs designed to assist with this, and when combined with features of Bash, and other Bash-like shells, the command line provides a comprehensive process control interface. In this tutorial, we'll cover some of the basics of process control from the command line. We'll use signals to interact with processes, and cover how to run processes in both the foreground, and background of a single terminal.
Processes can be controlled by signals, which are precisely what they sound like. Some signals, like KILL
and STOP
, cannot be ignored by processes, however most may be affected by the program's defined handling.
Here are some common signals, their x86 numbers, and a brief description of what they do:
(2) SIGINT - Keyboard (terminal) Interrupt, can usually be sent to foreground process with ^c
(3) SIGQUIT - Keyboard Quit, can usually be sent to foreground process with ^\
(9) SIGKILL - Terminate the process
(11) SIGSEGV - Sent by kernel when process generates a segmentation fault
(18) SIGCONT - Continue stopped process
(19) SIGSTOP - Stop the process
(20) SIGSTSP - Keboard Stop, can usually be sent to foreground process with ^z
Sending SIGTERM
with ^c
is common way to terminate, though messily, a running process:
$ sleep 60
^c
In Bash, the jobs
builtin allows you to view what processes have been suspended, for instance by SIGSTSP
(^c
). They can be resumed in either the foreground (takes control of terminal) or in background (doesn't take control of terminal) using the builtins fg
and bg
respectively:
$ sleep 60
^z
[1] + 16254 suspended sleep 60
$ jobs
[1] + suspended sleep 60
$ fg
It's also possible to have jobs
report the pid of processes with the -l
switch, just like what's displayed when we suspend a process:
$ sleep 60
^z
[1] + 16254 suspended sleep 60
$ jobs -l
[1] + 16254 suspended sleep 60
$ fg
Jobs can be started in the background by appending &
to the end of the command:
$ sleep 60 &
[1] 19934
$ jobs
[1] + running sleep 60
To better illustrate the difference between processes in the foreground and background, let's write a little toy script that reports its pid every so often:
#!/bin/bash
# $$ holds the process' pid
# $1 is the first argument passed ($n is argument n where 0 is the command itself)
for i in {1..60}; do
echo Hey\, $$ here!;
sleep $1;
done
Let's save that in a file called echo.sh and make it executable with chmod
like so:
$ chmod +x echo.sh
Now let's try starting echo in the background like we did with sleep
. To terminate the program we can bring it to the foreground with fg
and then stop it with ^c
:
$ ./echo.sh 3 &
[1] 23828
$ Hey, 23828 here!
Hey, 23828 here!
...
$ jobs
[1] + running ./echo.sh 3
Hey, 23828 here!
$ fg
[1] + 23828 running ./echo.sh 3
^c
Now let's start two of them, both in the background. Commands can be strung together by seperating them with a ;
:
$ ./echo.sh 2 &; ./echo.sh 3 &
[1] 25053
[2] 25054
Hey, 25053 here!
Hey, 25054 here!
...
$ jobs
[1] - running ./echo.sh 2
[2] + running ./echo.sh 3
Hey, 25053 here!
Hey, 25054 here!
$ fg
[2] - 25054 running ./echo.sh 3
Hey, 25053 here!
^C
$ fg
[1] - 25053 running ./echo.sh 2
Hey, 25053 here!
^C
A little messy, but interesting!
Kill Command
During administration, the most common way to send a signal to a process that's not in the foreground of the terminal you're working with is to use the kill
command. Despite its name, it can do more than just send a SIGKILL
.
When only passed a process identifier (pid), kill sends it SIGTERM
:
$ sleep 60 &
[1] 16840
$ kill 16840
[1] + 16840 terminated sleep 60
If we suspend a process by sending it a SIGSTSP
with ^c
, and then try to kill it with a SIGTERM
, nothing happens:
$ sleep 10
^z
[1] + 27994 suspended sleep 10
$ jobs -l
[1] + 27994 suspended sleep 10
$ kill 16254
$ jobs -l
[1] + 27994 suspended sleep 10
This is because a stopped process can only be affected by SIGKILL
or SIGCONT
. Other signals, like SIGTERM
, are queued however, and will be delivered to the process upon continuation:
$ sleep 60
^z
[1] + 29062 suspended sleep 60
$ jobs -l
[1] + 29062 suspended sleep 60
$ kill 29062
$ jobs -l
[1] + 29062 suspended sleep 60
$ fg
[1] + 29062 continued sleep 60
[1] + 29062 terminated sleep 60
It's possible to achieve the same thing by using %
, which in Bash is the special character for job identity. Functionally, it refers to the first job in the jobs list, and can be used by kill
instead of a pid:
$ sleep 60
^z
[1] + 30853 suspended sleep 60
$ kill %
[1] + 30853 terminated sleep 60
We can send arbitrary signals with kill
by either name or number:
$ sleep 60
^z
[1] + 31122 suspended sleep 60
$ kill -9
[1] + 31122 killed sleep 60
...
$ sleep 60
^z
[1] + 31516 suspended sleep 60
$ kill -KILL
[1] + 31516 killed sleep 60
A nicely formatted table of the signals and their corresponding number can be produced by kill
with the -L
switch. However, because Bash has a builtin called kill that doesn't support -L
, we need to specifically run the binary:
$ /bin/kill -L
1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS
8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM
15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN
22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH
29 POLL 30 PWR 31 SYS
Notes and Further Reading
ps
is useful in figuring out what's running and who's running it.pgrep
andpkill
can be used to either find the pid of a program, or kill a program by pattern.- For a very complete overview of available signals, how to use them, caveats and programming information, see (in Linux) the Programmer's Manual entry:
man 7 signal
- The libc manual's section on Job Control Signals is also quite handy!