# Process lifecycle

_Peios / Using Peios / Threads and processes_

> How a process ends, how its result is collected, what happens when its parent ends first, and how the system cleans up after it.

Every process eventually ends. A process's life has three parts — it is
created, it runs, and it finishes — and the system ensures that when it
finishes, its result is collected and its resources are reclaimed.

## The states it passes through

While it exists, a process is in one of a handful of **states**, and the system
moves it between them. Most of the time it is either **running** — executing,
or ready to run the moment a core is free — or **sleeping**, paused while it
waits for something to happen, such as data to arrive or a timer to fire. A
sleeping process uses no processor time until what it is waiting for is ready;
most processes spend most of their lives asleep.

A few states are worth knowing by name:

- **Stopped** — suspended (for example by job control when a job is paused at
  the terminal); it does nothing until told to resume.
- **Traced** — stopped under the control of a debugger that has attached to it, so
  the debugger can inspect and step it.
- **Uninterruptible sleep** — waiting on something the system will not interrupt,
  almost always a brief piece of disk or device I/O. A process here cannot be woken
  or even killed until the operation finishes; it normally lasts an instant, and a
  process stuck in it is a sign that hardware or a filesystem is stuck too.
- **Zombie** — finished, but still held as a result waiting to be collected (see
  below).

Once its result has been collected the process is gone, removed from the system
entirely.

## Two ways a process ends

A process ends in one of two ways.

Most often it ends **on its own**: it finishes its work and stops. A process
that ends this way leaves behind an **exit status** — a small record of how it
went, usually just "succeeded" or "failed" (and, on failure, a number giving a
rough reason). The act of a process ending itself is called **exit**.

The other way is that a process is **stopped from outside** — something tells
it to stop, and it ends whether or not its work was done. (The messages that
do this are **signals**, a subject of their own.) A process ended this way
also leaves an exit status, marked to show it was stopped rather than
finishing on its own.

Either way, a record of the process remains until its result is collected.

## Collecting the result

Whenever a child changes state — ends, is stopped, or resumes — the system
sends its **parent** a signal, `SIGCHLD`, prompting it to check. The parent
then reads the child's exit status. This is called **waiting** for the
process.

Between the moment a process ends and the moment its parent collects the
result, the finished process is a **zombie**: no longer running, but with the
system still holding its exit status so the parent can read it. Despite the
name, a zombie is harmless — it does no work and uses almost nothing; it is a
result waiting to be collected. The instant the parent collects it, the zombie
is gone for good.

If a parent never collects, zombies accumulate — finished processes whose
results no one read. That is a fault in the parent, not normal behaviour.

A parent holding a [pidfd](/peios/using-peios/threads-and-processes/creating-processes.md) for its
child can wait on it directly — the dependable way to be told the exact moment the
child ends.

The exact calls — every `wait` variant, and how an exit status is read — are in the
[process lifecycle reference](/peios/using-peios/threads-and-processes/process-lifecycle-reference.md).

## When the parent ends first

A parent does not always outlive its children. If a parent ends while one of
its children is still running, that child becomes an **orphan**.

Orphans are not lost. The system's first process — PID 1, which on Peios is
**peinit** — adopts every orphan and stands in as its parent from then on:
when the orphan eventually ends, peinit collects its result, so no finished
process is ever left uncollected. Adoption is automatic; the orphan keeps
running unaffected, only with a new parent.

PID 1 is the catch-all, but a process can arrange to catch orphans within its
own subtree. By marking itself a **subreaper** (with
`prctl(PR_SET_CHILD_SUBREAPER)`), a process becomes the one that adopts any
orphan descended from it — its grandchildren and below — instead of letting
them reparent all the way up to PID 1. Service managers and container managers
use this to keep responsibility for everything they launched: when something
deep in their subtree loses its immediate parent, it reparents to the manager,
which still learns when it finally ends.

## What the system cleans up

When a process ends, the system reclaims what it was using. Its private memory is
freed and its open files and connections are closed.

It also **releases its identity**. A process acts as a principal, carried on
its token; when the process ends, its hold on that token is released. If other
processes were sharing the same identity — for example, siblings from the same
login — the identity lives on for them, and the system clears it away only once
nothing is using it any more. The details are in
[Token lifecycle](/peios/security-fundamentals/tokens/lifecycle.md).

The process's PID becomes available for the system to assign to a future
process. Its Process GUID is not reused — it remains a permanent marker of
that one process, so records of what it did still point unambiguously back to
it long after it is gone.

## Where to go next

Processes are arranged into a family tree and grouped together in ways that
matter for running them. That is
[Process relationships and job control](/peios/using-peios/threads-and-processes/relationships-and-job-control.md).

Related content:

- [Creating processes](/peios/using-peios/threads-and-processes/creating-processes.md)
- [Process relationships and job control](/peios/using-peios/threads-and-processes/relationships-and-job-control.md)
- [Token lifecycle](/peios/security-fundamentals/tokens/lifecycle.md)
