#7: Job control and background processes
Linux is a multitasking operating system, so you can execute several programs at once. It is also possible to make use of this on the console. Normally when you run a program, you have to wait until the program has finished to get your shell prompt back. Therefore, during the execution of a program the shell is “locked”. But it isn't unresponsive as you can see when you press CTRL+C
, which sends a SIGINT to the running process. Hence, the shell is not really “locked” but rather “occupied” and you can perform operations while the program is being executed.
Besides Ctrl+C
, there is another keyboard shortcut you should definitely know about and that is Ctrl+Z
. Ctrl+Z
sends a SIGTSTP to the currently running process. Unlike SIGSTOP, this signal can be handled by the process and does not cause the process to stop necessarily, but in most cases, when you press Ctrl+Z
, you'll see something like that:
$ ^Z [1]+ Stopped foobar
This indicates that the process foobar
has just been stopped and is not executed anymore. But it is not terminated. Stopped processes are still alive and marked with a T
in the STAT
column of ps
. You can continue a stopped process at any time by entering %N
, where N
is the number in square brackets from above (here it is 1
, so %1
would continue the process). This number is called the job ID, which is assigned to each stopped process by the shell's job control.
You may use this job ID to continue the process, but you don't have to do this in the foreground. You may also continue executing it in the background, so you can use the shell while the process is being executed concurrently. To send a stopped process to the background, run
bg N
Again, N
is the job ID (if you don't pass any, the job ID of the last stopped process will be used). Background processes keep their job ID so you can still interact with them even though they don't occupy the shell (note: you still may see STDOUT of the backgrounded process but if you hit enter you see that your shell prompt is still there). To see all processes being stopped or running in the background, use jobs
. E.g.
$ jobs [1]+ Running dd if=/dev/zero of=/dev/null &
This shows you that there is a process running in the background, continuously copying zeros from /dev/zero
to /dev/null
(this is supposed to run forever and heat up your CPU, so don't run this command longer than needed ). You might also have noticed the ampersand &
at the end of the command name. This is a shell operator to send processes into background. Thus, instead of running a command, tapping Ctrl+Z
and then invoking bg
you could simply use the ampersand operator:
dd if=/dev/zero of=/dev/null &
This process would directly go into background. You can check that with jobs
.
It is also possible to bring backgrounded processes back to foreground. The command for this is fg
. As well as bg
, the command fg
takes the job number as argument. If no argument is passed, the last backgrounded or stopped process will be brought to foreground. In doing so, stopped processes will continue execution. fg
is completely equivalent to %
. Both bring stopped or backgrounded processes back to foreground, and both may be used with or without an explicit job ID.
fg
and bg
are the shell's default commands for job control but, as mentioned before, they just use signals. Job control is not really a Linux thing. The kernel itself only provides multitasking and process groups and handles signals, but managing job IDs and foreground/background processes is the shell's business (therefore, job IDs are not available across multiple shell instances). The signal for stopping processes is SIGTSTP or the uncatchable SIGSTOP and the signal for continuing them (in background) is SIGCONT. Let's try this. Run this command (or any other endless running or long lasting command):
$ sleep 900 & [1] 3456
This will sleep for the next 15 minutes peacefully in the background. The output of this command is the job ID in brackets and the PID of this background process. Now control the execution state of this command with jobs
:
$ jobs [1]+ Running sleep 900 &
If jobs
outputs something like that you've done all right. To stop this process now, either bring it back to foreground and hit Ctrl+Z
or send a SIGTSTP manually:
$ kill -TSTP 3456 [1]+ Stopped sleep 900
To continue the process, we send a SIGCONT but this time we don't pass the PID but the job ID to kill
:
kill -CONT %1
Control with jobs
:
[1]+ Running sleep 900 &
Great, process is running again!
Jobs, i.e., via job control stopped and backgrounded processes, depend on the opened shell instance since they are handled in process groups being children of the current shell process. When the shell is terminated by sending it a SIGHUP, this signal will be resent to all subprocesses. Login shells also send a SIGHUP to their children when leaving with the exit
or logout
command if huponexit
is set. Because of this, jobs probably don't survive closing the shell. But there is one command to avoid this behavior: nohup
. nohup
(which stands for no hangup) let's you make sure that a process does not terminate when closing the shell window. To run our sleep
process in the background with nohup
, type
nohup sleep 900 &
You can now close your terminal without terminating our process. All output of our program will be written to a file nohup.out
within the current working directory (but since our program is not snoring there's no output here). What nohup
does, is actually no magic. It just changes the signal disposition attribute of this process for SIGHUPs to Ign
, so hangup signals are ignored.
One thing which is good to know is that when a job has finished, its job information will still be kept in memory until you run jobs
or close the terminal/tty. If there is a finished job, jobs
would output something like that:
$ jobs [1]+ Done sleep 1
That's basically it, but I have one great warning in the end: be careful with background processes and sudo
! If you use the ampersand operator to send a process into background, you'll see nothing more than nothing and jobs
shows you why:
$ jobs [1]+ Stopped sudo foobar
Your process is stopped because sudo
is waiting for your password. You have to bring this process back to foreground for entering it. However, the more crucial issue is that due to the shell-dependency terminating “sudoed” background processes with SIGINT, SIGTERM or even SIGKILL by addressing their job ID is not possible. You would get an error
bash: kill: (4567) - Operation not permitted
This is because the sudo
process is owned by root; thus, you have to kill this process with root privileges and that closes the circle: you can't address job IDs from within temporary sudo
environments. You need a real shell environment for that but sudo -s
or su
would open a completely new shell instance, so you can't address the parent shell's job IDs either. Hence, you have to kill those processes as root by directly addressing their PIDs. To examine the corresponding PIDs, either use ps
, jobs -l
or jobs -p
to only show PIDs. The result of jobs -p
can directly be passed to kill and would therefore kill all jobs of the current shell instance. For instance:
$ sudo kill `jobs -p` [2]+ Exit 143 sudo foo [1]+ Exit 143 sudo bar
Note: if sudo
is still waiting for your password at this time, you have to bring it to the foreground first or you must send a harsh SIGKILL (infamous signal No. 9). SIGINT or SIGTERM would not terminate sudo
in that case because those signals can be ignored.
You see, using sudo
in combination with your shell's job control functions is sometimes very delicate. One quick tip here: sudo
itself provides a parameter -b
, which sends the process into background. This is probably the better choice here since it makes things clearer and sudo
also doesn't wait forever unnoticed in the background if it waits four your password. Just keep that in mind!
Read more about job control:
- About.com: bg man page
- About.com: fg man page
- About.com: jobs man page
- die.net: signal man page
- Wikipedia.org: SIGTSTP
- Wikipedia.org: SIGSTOP
- Wikipedia.org: SIGCONT
- StackOverflow.com: Difference between process group id and job id in UNIX
New Blog entry: #7: Job control and background processes http://bit.ly/fTu8Df