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.