Job control in Shell
In this section, we will explain the concepts of sessions and process groups, which exist for the implementation of shell's job control.
For those unfamiliar with jobs, a job is a mechanism that shells like bash use to control processes running in the background. For example, you might use it as follows:
$ sleep infinity &
[1] 6176 # [1] is the job number
$ sleep infinity &
[2] 6200 # [2] is the job number
$ jobs # List the jobs
[1]- Running sleep infinity &
[2]+ Running sleep infinity &
$ fg 1 # Make job 1 the foreground job
sleep infinity
^Z # Press Ctrl+z and control returns to bash
[1]+ Stopped sleep infinity
Sessions
A session corresponds to a login session when a user logs into the system through a terminal emulator like gterm
or ssh
. All sessions have a terminal attached for controlling the session. When you want to operate processes within the session, you instruct them through the terminal to processes, including the shell, and receive the output from these processes. Normally, a virtual terminal named pty/<n>
is assigned to each session.
Let's consider a situation where three sessions exist:
- Alice's session: The login shell is
bash
. Developing a Go program withvim
on it, and currently building some program withgo build
. - Bob's session 1: The login shell is
zsh
. Usingps aux
on it to list all processes in the system, and receiving the results withless
. - Bob's session 2: The login shell is
zsh
. Running a custom calculation program calledcalc
on it."
This situation can be illustrated as follows:
Each session is assigned a unique value, called a "session ID" (or "SID"). Each session has a process called a session leader, which is typically a shell like bash
. The PID of the session leader equals the ID of the session. Information about the session can be obtained by ps ajx
. In the author's environment, it is as follows:
$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
...
19261 19262 19262 19262 pts/0 19647 Ss 1000 0:00 -bash
...
19262 19647 19647 19262 pts/0 19647 R+ 1000 0:00 ps ajx
...
In this case, we can see that there is a session (SID=19262) with bash(19262)
as the session leader, and ps ajx
(PID=19647) belongs to this session. Commands launched from bash(19262)
normally belong to this session. The TTY
field in ps ajx
, and in ps aux
used in previous sections, is the name of the terminal. In this session, a virtual terminal pts/0
is assigned.
When the terminal associated with the session hangs up, a SIGHUP
is sent to the session leader. This happens when the terminal emulator's window is closed. bash
will terminate its managed jobs and then terminate itself in this case. For cases where it would be a problem if bash
terminated while a long-running process was executing, the following measures can be used:
- The
nohup
command: Launches a process with the setting to ignoreSIGHUP
. Even if the session terminates and aSIGHUP
is sent afterward, the process does not terminate. - The
disown
built-in command ofbash
: Removes a running job frombash
's management. As a result, even ifbash
terminates, aSIGHUP
will not be sent to the job.
Process Groups
Process groups are used to control multiple processes collectively. Within a session, there exist several process groups. Essentially, you can think of the jobs created by the shell as corresponding to process groups1.
To be more precise, the shell itself also has its own process group, but for the sake of simplicity, we will omit this from the current discussion.
Let's illustrate process groups with an example. Suppose a session is set up as follows:
- The login shell is
bash
. - From the above
bash
,go build <source name> &
is executed. - From the above
bash
,ps aux | less
is executed.
In this case, bash creates two process groups (jobs) corresponding to go build <source name> &
and ps aux | less
.
With process groups, signals can be thrown to all processes belonging to a certain process group. The shell uses this feature for job control. If you specify a negative value for the process ID argument of the kill
command, you can send a signal to the process group. For example, if you want to send a signal to a process group with a PGID of 100, you can do so with kill -100
.
Process groups within a session can be divided into two types:
- Foreground process group: Corresponds to the foreground job in the shell. Only one exists in a session and has direct access to the session's terminal.
- Background process group: Corresponds to the background job in the shell. When a background process tries to operate the terminal, it temporarily suspends execution as when it receives a
SIGSTOP
, and this state continues until it becomes a foreground process group (or a foreground job) by commands such as thefg
built-in command.
The latter, the foreground process group (foreground job), is the one that can access the terminal directly. This is illustrated as follows.
Each process group is assigned a unique ID known as a PGID. This value can be confirmed by the PGID
field in ps ajx
. In my environment, it looks like this:
$ ps ajx | less
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
...
19261 19262 19262 19262 pts/0 19653 Ss 1000 0:00 -bash
...
19262 19653 19653 19262 pts/0 19653 R+ 1000 0:00 ps ajx
19262 19654 19653 19262 pts/0 19653 S+ 1000 0:00 less
...
From the output, we can see that there is a login session led by bash(19262)
, within which there is a process group with a PGID of 19653. The constituents of this group are ps ajx(19653)
and less(19654)
, which is piped to it.
Lastly, let me also mention how to distinguish foreground process groups. In the output of ps ajx
, the processes belonging to the foreground process group have a +
in their STAT
field.
The concepts of sessions and process groups can be challenging,
Daemons
You might have heard the term "daemon" in the context of UNIX or Linux countless times. This section will discuss what daemons are and how they differ from regular processes. Simply put, daemons are resident processes. While regular processes are expected to terminate after completing a series of operations initiated by a user, daemons do not necessarily behave this way and may persist from system start to finish, depending on the case.
Daemons have the following characteristics:
- They do not require terminal I/O, so no terminal is assigned to them.
- They possess their own session to remain unaffected even if all login sessions end.
- Init acts as their parent process so that the process generating the daemon does not need to worry about the daemon's termination.
If illustrated, it would look like this:
However, even if they do not meet the above conditions, they may be referred to as daemons if they are resident processes.
You can determine whether a process is a daemon by looking at the results of ps ajx
. Let's take a look at sshd
, which operates as an ssh server.
$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
...
1 960 960 960 ? -1 Ss 0 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
...
Indeed, the parent process is init (PPID is 1), and the session ID equals the PID.
Because daemons do not possess a terminal, the SIGHUP, which signifies a terminal hang-up, can be utilized for different purposes. By convention, it is often used as a signal for daemons to reread their configuration files.
NOTE
This article is based on my book written in Japanese. Please contact me via satoru.takeuchi@gmail.com if you're interested in publishing this book's English version.
Top comments (1)
thanks you so much. extremely good article. I am tuned to your series of articles ahead.