Dealing with process termination in Linux (with Rust examples)
A l ong one this right time, so directly to the idea! First, we are going to discuss the Linux processes trivia and then review the scenarios that are following as usually, with some rule examples
- waiting for a son or daughter procedure termination;
- awaiting a grandchild procedure termination;
- catching the moms and dad process termination.
Linux processes fundamentals
First of all, every process in Linux comes with an ID, so-called PID. Once we create a brand new process via fork() [or clone()] system call, a next extra PID is assigned to it by the kernel. The procedure which makes the fork() call becomes a parent associated with the newly developed process and its PID becomes a process that is parent, i.e. PPID of this young kid procedure. This procedure is respected for all your processes within the system, starting from the extremely first one (i.e. init process with PID 1). Hence, processes in Linux form a tree-like structure where at any provided time, an activity constantly possesses solitary parent procedure (ancestor) and may have from 0 to N youngster processes (descendants)
Regarding the screenshot above it has an production of ps –ppid 2 -p 2 –deselect -fo pid,ppid,command showing the present state of this procedure tree for a Centos 7 machine. The init process, since it frequently occurs in modern Linux distributions, is implemented by systemd . Also, we intentionally utilized –ppid 2 -p 2 –deselect to prevent spoiling the outcome with all the many kernel threads spawned by the procedure with PID 2. Notice, the branches started by sshd daemon with PID 3164. The daemon it self is started by the init process with PID 1. In change, it spawned two subprocesses 6746 and 6957 for the two ongoing sessions that are ssh this host. And these sessions in their turns spawned bash shells with PIDs 6750 and 6961.
Awaiting kid procedure termination
To begin with, why do we must wait? Well, sometimes the parent procedure is simply thinking about the youngster termination event. The famous instance is a worker p l pattern employed by Nginx, PHP-FPM, uWSGI, among others. The key procedure has to keep a fixed-size p l of sub-processes, even though the processes within the p l are doing the payload that is actual. Sporadically, employees die, and also the primary procedure has to spawn more procedures in reaction to keep the p l size unaltered. With this and similar situations, the Linux kernel offers a group of wait() system calls to await the little one procedure termination. But, because of the way, the reporting for the son or daughter exit status back once again to the parent is implemented, also careless parents need to explicitly await kids terminations. Otherwise, a zombie apocalypse begins.
When a process forks itself, the execution for the created child process takes place individually (we are going to leave process groups and job control out of the range of this article) and concurrently because of the parent’s execution. Even though the process that is new still parented to your initial process, by default its execution just isn’t synchronized with it. Considering that the Linux kernel needs to always supply a way to report the child exit status (and some use stats, like invested CPU cycles, number of page faults, etc) back to its parent, the kernel has to keep hardly any information regarding every pop over here process in system memory even after its exit. These details gets washed up only if it was clearly l k over by the corresponding moms and dad procedure. The process stays in defunct or zombie state from the process termination and until its status has been read by the parent via wait() call. Zombie procedures keep system resources, such as for example process or memory ids, consumed and that fundamentally leads to the denial of solution.