Skip to main content
RunBook Academy

LinuxVI · ProcessesProcess model

The Linux process model — PIDs, PPIDs, sessions, and groups

Foundation⏱ ~10 minbashpstoppstree

What you'll learn

  • Explain PID, PPID, PGID, SID, and TID and the relationships between them
  • Use ps to read process state and ancestry
  • Distinguish a process, a thread, and a cgroup view
  • Identify zombie and orphan processes and what they mean

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

Every running program on Linux is a process, an instance of an executable under execution. The kernel tracks each process with a set of identifiers; understanding them is the vocabulary for every later troubleshooting lesson.

The identifiers

IdentifierMeaning
PIDProcess ID — unique on the host, allocated sequentially (with reuse) by the kernel
PPIDParent PID — the PID of the process that created this one
PGIDProcess Group ID — used for job control and signal delivery
SIDSession ID — the leader of a session; usually the login shell or service manager
TIDThread ID — within a process, the kernel also tracks each thread
TGIDThread Group ID — same value as the leader thread’s TID, used to find all threads in a process

Every schedulable entity the kernel knows about has a TID. Threads that belong to the same process all share one TGID, and that TGID is the TID of the group’s leader thread — it is also the number userspace calls “the PID”. So the rule reads in one direction only: same TGID with different TIDs means threads of one process; different TGIDs means different processes.

From the shell’s perspective, ps -eLf prints one row per TID (every thread); ps -ef prints one row per TGID (leader threads only). Per-thread state lives under /proc/<TGID>/task/<TID>/. This matters when you signal something: kill acts on the whole thread group, not on the individual thread whose TID you read out of a stack trace.

Read-only / Safeps -eo
$ ps -eo pid,ppid,pgid,sid,comm | head
  PID  PPID  PGID   SID COMM
1     0     1     1 systemd
124     1   124   124 systemd-journal
135     1   135   135 systemd-udevd
234     1   234   234 sshd
12345  234 12345 12345 bash
12346 12345 12346 12345 ps

Illustrative output

Process states

The kernel tracks a per-process state in /proc/<pid>/status and in ps output:

StateMeaning
RRunning (on a CPU or in the run queue)
SSleeping (interruptible — waiting for an event)
DDisk sleep (uninterruptible — typically waiting for I/O)
ZZombie (terminated but not reaped by parent)
TStopped (suspended by signal or debugger)
IIdle (kernel thread, waiting for work)
XDead (very transient)
Read-only / SafeD-state
$ ps -eo pid,stat,comm | grep -E '\b(D|Z)\b' | head
  PID STAT COMM
5678 D   kworker/1:2

Illustrative output

Zombie processes

A zombie is a process that has finished execution but whose parent has not yet reaped it. The kernel keeps the PID and a minimal record so the parent can read the exit status. Once the parent calls wait() (or waitpid()), the zombie is released.

Read-only / Safezombie
$ ps -eo pid,ppid,stat,comm | grep 'Z' | head
  PID  PPID STAT COMM
12345     1 Z   myapp <defunct>

Illustrative output

A few zombies are normal. Hundreds are a sign that something is not calling wait(). Zombies do not consume memory beyond a small kernel struct; they do consume a PID. The PID space is finite: kernel.pid_max defaults to 32768 on older kernels but is commonly 4194304 on modern 64-bit systems, so read cat /proc/sys/kernel/pid_max on the host in front of you rather than assuming a number. If a process leaks zombies faster than they are reaped, eventually fork() returns EAGAIN and new processes cannot start.

Orphan processes

A process whose parent exits before the child is orphaned. The kernel reparents it immediately — synchronously, inside the exiting parent’s own teardown — so there is no window in which ps shows a stale PPID.

The new parent is not always PID 1. The kernel walks up the ancestry looking for the nearest process marked as a child subreaper (prctl(PR_SET_CHILD_SUBREAPER)), and only falls back to PID 1 if there is none. On a systemd host that ancestor is frequently something else: systemd --user is a subreaper for every user session, and container runtimes and application supervisors set the flag too.

That makes an orphan with PPID != 1 a signal rather than noise. It tells you which supervisor adopted the process and is therefore the one responsible for reaping it — which is where you go looking when the zombies start piling up.

Read-only / Safeorphan reparenting
$ ps -eo pid,ppid,comm | head -10
  PID  PPID COMM
1     0 systemd
567     1 cron
12345 12344 myapp

Illustrative output

Process ancestry with pstree

Read-only / Safe
$ pstree -p | head -10
systemd(1)-+-accounts-daemon(567)-+-{accounts-daemon}(568)
|                       `--{accounts-daemon}(569)
|-chronyd(890)
|-cron(910)
|-dbus-daemon(945)-+-dbus-daemon}(946)
|                   `-{dbus-daemon}(947)
|-irqbalance(1056)-+-{irqbalance}(1057)
|                  `-{irqbalance}(1058)
|-networkd-dispat(1102)-+-{networkd-dispat}(1103)
|                       `-{networkd-dispat}(1104)
|-nginx(12345)-+-nginx(12346)
|              |-nginx(12347)
|              `-nginx(12348)
|-rsyslogd(1500)-+-{rsyslogd}(1501)
|                `--{rsyslogd}(1502)
|-sshd(1700)---sshd(2345)---bash(12340)---pstree(12341)

Illustrative output

Knowledge check

Knowledge check · 3 questions

  1. Q1. What does state D (uninterruptible sleep) mean for a process?

  2. Q2. A zombie process can be killed with SIGKILL.

  3. Q3. Which of the following are true about process ancestry on Linux? Select all that apply.

Passing score: 75%. Answers are checked in this browser.