17. Job Control

Tasks are done within UNIX like systems by processes, sharing system resources in a concurrent manner. All commands except the builtin commands of Bash and commands told to do so are executed in a separate process also. The ps command shows the actual running processes. The following print is a subset of processes:

$ ps -eo pid,ppid,uid,comm
 PID  PPID   UID COMMAND
    1     0     0 init
 1023     1     0 lightdm
 1576  1023     0 lightdm
 1679  1576  1000 gnome-session
 1756  1679  1000 compiz
 2054  1756  1000 sh
 2055  2054  1000 gnome-terminal
 4025  2055  1000 bash
 4136  4025  1000 ps

The processid pid is a positive integer. It is used to manage the process. The ppid is the processid of the parent process. New processes (childs) are started by an existing process (parent). The ps command with the processid 4136 was started by the Bash process with the processid 4025. Bash itself was started by the terminal program gnome-terminal with processid 2055. The ancestor of each process is the first process, init with pid 1. The consumption of system resources by a process can be displayed by the time key word:

$ time sleep 2

real    0m2.168s
user    0m0.046s
sys     0m0.031s

The sleep command terminates after a given time without doing anything else. The command was run within 2.168 seconds. The values of system and user time are proportional to the cycles of the CPU (wall clock) which were done during running the command. User time was needed to process the command while the System time elapsed. The consumption of system resources per time can be lowered by the nice command:

$ nice -9 sleep 2

Values from 0..19 lower the consumption of system resources per time. nice is used for long running tasks with a low priority. Bash executes a list of commands synchronous - one after another. This guarantees that the next command will process the results of the previous command (see pipes). Commands are started asynchronous by the "&" operator:

$ sleep 2000 & 
[1] 6900
$

Asynchronous started commands are called jobs. 1 is the job number, 6900 the pid of the job. Asynchronous commands are started in an own process and Bash doesn't wait until the command succeeds. Bash proceeds with the execution of the next command immediately. All jobs can be listed by the jobs command:

$ jobs
[1]+  Running                 sleep 2000 &

Asynchronous execution does a more efficient use of system resources by it's concurrent behavior. But any kind of order will get lost. To ship back into synchronization, the wait command is used:

$ sleep 2 &; wait; echo done;
[1] 3783
[1]+  Done                    sleep 2
done

The termination of a process hangs up its childs. To proceed with a command even through termination of its parent, the nohup (no hang up) command is used:

$ nohup sleep 2 &
[2] 7200
$ exit

By use of nohup the stdin is redirected from /dev/null and the output is redirected to nohup.out by default:

$ sleep 2 </dev/null > nohup.out

Jobs are managed by signals. The signals are sent to the processes by inter process communication. But most signals can be ignored. Otherwise, they interrupt the program and invoke a signal handler. In most cases, the default handler can be overwritten by a custom handler. After completion of the handler, the program continues. The following list shows common signals:

$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGEMT       8) SIGFPE       9) SIGKILL     10) SIGBUS
11) SIGSEGV     12) SIGSYS      13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGURG      17) SIGSTOP     18) SIGTSTP     19) SIGCONT     20) SIGCHLD
21) SIGTTIN     22) SIGTTOU     23) SIGIO       24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGPWR      30) SIGUSR1
31) SIGUSR2     32) SIGRTMAX

Signals are sent by the kill command. Either the pid or the jobspec, %jobnumber, are used to address the job. Either the signal name (SIGHUP) or its numerical representation (1) can be used. To stop the execution of a process the SIGSTOP signal is sent best, because it can't be caught or ignored:

$ sleep 2000 &
$ kill -SIGSTOP %1
$ jobs
[1]+  Stopped                 sleep 2000

To restart the job, the SIGCONT signal is sent:

$ kill -SIGCONT %1
$ jobs
[1]+  Running                 sleep 2000 &

The best way to terminate a job is sending the SIGKILL signal. SIGKILL can't be caught or ignored:

$ kill -SIGKILL %1
$ jobs
[1]+  Killed                  sleep 2000

Bash supports the built-in command trap to react on signals:

$ trap 'echo "receive signal"'  SIGINT
$ echo $$ 
4536

Now within a second terminal a signal will sent to the first terminal with pid 4536:

$ kill -SIGINT 4536

The somehow crypted message will be printed to the first terminal:

$ ^Creceive signal

Signals can be ignored by calling trap with the NULL argument and the signal name:

$ trap '' SIGHUP

If SIGHUP is sent from the second terminal:

$ kill -SIGHUP 4536

the first terminal won't be closed. To unset trap is called without argument, but with the signal name:

$ trap SIGHUP

Sending SIGHUP again from the second terminal will close the first terminal. Trap doesn't listen to signals only. It reacts on the call of the exit command as well as on the return command:

$ trap 'echo "receive builtin call"' EXIT RETURN

as well as on commands that exits with a status not equal 0:

trap 'echo $? >> log' ERR

To list currently all set traps, trap is called without any argument:

$ trap
trap -- 'echo "receive builtin call"' EXIT
trap -- 'echo "receive signal"' SIGINT
trap -- 'echo $? >> log' ERR
trap -- 'echo "receive builtin call"' RETURN

Some signals can be sent manually by the keyboard to the actual running foreground process. SIGSTOP suspends a process by pressing [CTRL]+[Z]:

 $ sleep 2000
^Z
[1]+  Stopped                 sleep 2000

SIGINT terminates the process almost. It can be sent by pressing [CTRL]+[C]. The fg and the bg commands can be used to put a process from background into the foreground, vice versa.