How Linux Works: Chapter2 Process Management (Part2)
Tags: linux, process, management
Published: 2023-06-10 04:41:38

---
title: How Linux Works: Chapter2 Process Management (Part2)
published: true
description: 
tags: linux, process, management
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-06-02 01:38 +0000
---

# The Relationship between Parent Process and Child Process

In the previous section, we discussed how a parent process generates child processes to create new processes. So where does one end up when following the parent process of the parent process...? This section will clarify this.

When you boot your computer, the system is initialized in the following order:

1.Turn on the power switch
2.Firmware, such as BIOS or UEFI, boots up and initializes the hardware
3.The firmware launches a bootloader like GRUB
4.The bootloader launches the OS kernel. Here we'll consider the Linux kernel
5.The Linux kernel starts the init process
6.The init process starts child processes, which in turn start their child processes, and so on, forming a tree structure of processes

Let's check if this is actually happening.

The `pstree` command displays the parent-child relationship of processes in a tree structure. `pstree` displays only command names by default, but it is useful to also display the PID by adding the `-p` option. In my environment, it looks like this:

```console
$ pstree -p
systemd(1)-+-ModemManager(688)-+-{ModemManager}(723)
           |                   `-{ModemManager}(728)
...
           ├─sshd(960)───sshd(19191)───sshd(19261)───bash(19262)───pstree(19638)
...
$
```

You can see that the ancestor of all processes is the init process with pid=1 (displayed as `systemd` on the `pstree` command). You can also see, for example, that the `pstree(19638)` was run from `bash(19262)`.

# States of Processes

In this section, we will discuss the concept of process states.

As already mentioned, there are always a large number of processes in a Linux system. Do these processes always use the CPU continuously? The answer is no.

The start time of a process running on the system, as well as the total amount of CPU time used, can be checked with the `START` field and `TIME` field of `ps aux`.

```console
$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
...
sat        19262  0.0  0.0  12888  6144 pts/0    Ss   18:24   0:00 -bash
...
```

From this output, it can be seen that `bash(19262)` started at 18:24 and has used almost no CPU time since then. The time at which I am writing this manuscript is around 20:00, so even though it has been over an hour since it was started, you can see that this process has used less than a second of CPU time. The same can be said for many other processes, which I will omit here.

So, what were these processes mainly doing after they started? They were sleeping, waiting for some event to occur without using the CPU. In the case of `bash(19262)`, it was waiting for user input, as there was nothing to do until the user input something. This can be seen from the STAT field of the ps output. A process with an `S` in the first character of the STAT field is in a sleep state.

On the other hand, a process that wants to use the CPU is said to be in a runnable state. At this time, the first character of STAT becomes `R`. When a process is actually using the CPU, it is said to be in a running state. How a process transitions between a running state and a runnable state will be discussed in the "Time Slices" and "Context Switches" sections of Chapter 3.

When a process terminates, it becomes a zombie state (STAT field is `Z`), and then it disappears. The meaning of the zombie state will be explained later.

The states of a process are summarized in the following figure.

![States of a process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/reay5ohfken9qn1bm0d3.jpg)

As can be seen from this figure, a process transitions through various states during its lifetime.

If all processes on the system are in a sleep state, what is happening on the logical CPU? In fact, at this time, a special process called an idle process that "does nothing" is operating on the logical CPU. The idle process is not visible from `ps`.

The simplest implementation of an idle process is to perform a wasteful loop until a new process is created or a sleeping process wakes up. However, this wastes power consumption, so it's not usually done. Instead, it uses a special CPU instruction to put the logical CPU into a sleep state, waiting in a state that reduces power consumption until one or more processes become runnable.

One of the main reasons why the battery lasts longer on your notebook PC or smartphone when you are not running any programs is because the logical CPU spends a lot of time in an idle state, which reduces power consumption.

# Process Termination

In this section, we'll discuss how a process is terminated by invoking a system call called `exit_group()`. Like `fork` or `fork-and-exec`, when you call the `exit()` function, this system call is internally invoked. Even if the program itself doesn't call it, libc or other libraries will do so internally. Within `exit_group()`, the kernel reclaims resources such as memory used by the process (see the following figure).

![Memory reclaim on process termination](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7lq6wzledce9jdlocbow.jpg)

After a process has terminated, the parent process can obtain the following information through system calls such as `wait()` or `waitpid()`:

- The process's return value. This is equal to the remainder when the argument to the `exit()` function is divided by 256. To make it clearer, if you specify a number between 0 and 255 as the argument to `exit()`, the return value will be the same as the argument.
- Whether the process was terminated by a signal (discussed later)
- How much CPU time the process used before termination

Through this mechanism, for example, you can handle anomalies such as outputting error logs if a process has terminated abnormally based on its return value.

In bash, you can obtain the termination status of a process that has been run in the background using the `wait` built-in command, which internally calls the `wait()` system call. Below, we run the `wait-ret.sh`  program, which retrieves and outputs the return value of the always-return-1 `false` command.

```wait-ret.sh
#!/bin/bash

false &
wait $! # wait for the termination of "false" process. We can get the PID of this program through `$!` variable.
echo "The false command has terminated: $?" # We can get the exit state of the process from `$?` variable.
```

"```console
$ ./wait-ret
The false command has terminated: 1
```

# Zombie Processes and Orphan Processes

The fact that a parent process can obtain the state of a child process through `wait()` system calls implies that, conversely, a child process exists in some form on the system from the time it terminates until its parent process invokes these system calls. A process that has terminated but whose parent has not obtained its termination status is called a zombie process. The name probably comes from the state of being 'dead but not dead', which is indeed a quite vivid term.

Generally, a parent process needs to appropriately reclaim the termination status of its child processes to prevent the system from overflowing with zombie processes and squandering resources. If there are a large number of zombie processes on the system during system operation, it may be worthwhile to suspect a bug in the program corresponding to the parent process.

If the parent of a process terminates before `wait()`, the process becomes an orphan process. The kernel makes `init` the new parent of the orphan process. If the parent of a zombie process terminates, the zombie process attacks `init`. This isn't a pleasant situation for `init`. However, `init` is smart and regularly issues `wait()` to reclaim system resources. It's quite a well-implemented system.

# Signals

Processes generally run continuously according to a single stream of execution. Although there are conditional branch instructions, these merely shift the flow according to predefined conditional statements. In contrast, a signal is a mechanism for a process to notify another process and forcibly change the flow of execution from the outside.

There are several types of signals, but the most commonly used is undoubtedly `SIGINT`. This signal is sent when you press `Ctrl+c` in a shell like `bash`. By default, a process that receives `SIGINT` terminates immediately. Regardless of how the program is structured, the ability to terminate a process the instant a signal is issued is convenient, and many Linux users use this signal, whether they are aware of its effect or not.

Signals can also be sent from outside `bash` using the `kill` command. For example, if you want to send `SIGINT`, execute `kill -INT <pid>`. In addition to `SIGINT`, there are signals like the following:

- `SIGCHLD`: Sent to the parent process when a child process terminates. It's common to call `wait()` within this signal handler.
- `SIGSTOP`: Temporarily suspends the execution of a process. Pressing `Ctrl+z` on `bash` halts the execution of the running program. At this time, `bash` is sending this signal to the process.
"- `SIGCONT`: Resumes the execution of a process that was stopped by `SIGSTOP` or similar.

You can see a list of signals by running the `man 7 signal` command.

As I wrote earlier that 'a process that receives `SIGINT` will terminate by default', it does not mean that a process will always terminate when it receives the `SIGINT` signal. A process can pre-register a signal handler for each signal. If the process receives the corresponding signal during execution, it temporarily interrupts the current operation, activates the signal handler, and then returns to the original location and resumes operation. Alternatively, it can be set to ignore the signal.

![Behavior of a process upon receipt of a signal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8wvyced2v6mneae3zwff.jpg)

By using a signal handler, you can create an annoying program (`intignore.py`) that does not terminate even when `Ctrl+c` is pressed. For example, you can create it in Python like this.

```intignore.py
#!/usr/bin/python3

import signal

# Set to ignore SIGINT
# - 1st arg: The signal to ignore
# - 2nd arg: signal handler
signal.signal(signal.SIGINT, signal.SIG_IGN)

while True:
	pass
```

When you run this program, it will look like this:

```console
$ ./intignore.py
^C^C^C
```

`^C` indicates that you've typed `Ctrl+c`. It's really annoying, isn't it?"

If you've tried this command yourself, please send `intignore` to the background with `Ctrl+z` and then kill it with `kill`. At this time, the default `SIGTERM` is thrown, so it can terminate.

# Column: The Absolutely Lethal SIGKILL Signal and the Absolutely Indestructible Process

`SIGKILL` could be considered the last resort to use when a process does not die gracefully by other signals like `SIGINT`. This is a special one among all signals, as a process that receives this signal is always terminated. It is not possible to change the behavior through a signal handler. From the signal name `KILL`, you can feel the strong intention to definitely kill the process.

However, after writing all this, there are occasionally nefarious processes that won't die, even with `SIGKILL`. For some reason, these processes are in a special state called `uninterruptible sleep`, where they do not accept signals for a long time. The first character in the `STAT` field of `ps aux` for these processes is `D`. This often occurs when disk I/O takes a long time. It may also be due to some problem with the kernel. In any case, there is often nothing that can be done from the user's side.

[previous part](https://dev.to/satorutakeuchi/how-linux-works-chapter2-process-management-part1-3n71)
[next part](https://dev.to/satorutakeuchi/how-linux-works-chapter-3-process-scheduler-part-1-115d)

# NOTE

This article is based on [my book](https://amzn.to/3K9q9Ke) written in Japanese. Please contact me via satoru.takeuchi@gmail.com if you're interested in publishing this book's English version.