Processes and ps
Every running program is a process with a PID and a PPID forming a tree rooted at init. ps aux and ps -ef give you two views of the process table: the first is user-centric, the second shows full command lines and parent relationships.
When you type a command and press Enter, the shell does not run that command itself — it forks a child process. That child inherits a copy of the shell’s file descriptors and environment, then calls exec to replace itself with the program you named. This fork-exec pattern is how every process on a Linux system comes to exist, except PID 1 (init/systemd), which the kernel starts directly.
The result is a tree: PID 1 at the root, everything else hanging below it. Understanding this tree — who spawned whom, which processes are consuming resources, which are orphaned or stuck — is the foundation for every signal, job-control, and monitoring task that follows in this unit.
After this lesson you can read ps aux output column by column, use ps -ef to trace parent-child relationships via PPID, distinguish foreground from background processes, identify zombie and orphan processes, and navigate /proc/<PID> for raw kernel data.
Every process has a PID (process ID) and a PPID (parent process ID). The kernel assigns PIDs sequentially. PID 1 is always init or systemd. All other processes are descendants: your shell is a child of a login session; every command you run is a child of your shell.
When a parent exits before its children, those children are re-parented to PID 1 — they become orphans adopted by init. When a child exits but its parent has not yet called wait() to collect the exit status, the child lingers in the process table as a zombie (Z state). Zombies hold no resources except a slot in the table, but many of them indicate a buggy parent.
echo $$ # PID of the current shell
echo $PPID # PID of the shell's parentps aux is the BSD-style snapshot of every process on the system. The columns you need to read fluently:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 22516 9216 ? Ss Jun20 0:03 /sbin/init
alice 1843 0.3 0.5 102400 41200 pts/0 S+ 10:12 0:00 vim notes.txt
alice 1901 98.0 1.2 204800 102400 pts/1 R 10:15 0:42 python train.py- VSZ — virtual memory size (includes shared libs, mmap’d files; often large and misleading)
- RSS — resident set size (pages actually in RAM right now; what you care about for memory pressure)
- STAT — one-letter state + flags:
Rrunning,Ssleeping (interruptible),Duninterruptible I/O wait,Zzombie,Tstopped. Suffix+means foreground process group;smeans session leader;lmeans multi-threaded. - TTY — the controlling terminal;
?means no terminal (daemon). - TIME — cumulative CPU time consumed, not wall-clock time.
ps aux
ps aux | grep nginx # filter to processes matching "nginx"
ps aux --sort=-%mem | head # top 10 by memoryps -ef is POSIX-style and adds PPID explicitly. Use it when you need parent-child tracing.
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jun20 ? 00:00:03 /sbin/init
root 512 1 0 Jun20 ? 00:00:00 /usr/sbin/sshd -D
alice 1801 512 0 10:11 pts/0 00:00:00 -bash
alice 1843 1801 0 10:12 pts/0 00:00:00 vim notes.txtRead this: sshd (512) was spawned by init (1). Your bash (1801) was spawned by sshd (512). vim (1843) was spawned by bash (1801).
ps -ef
ps -ef | grep <PID> # find parent of a specific process
ps -ef --forest # ASCII tree (GNU ps)
pstree -p # graphical tree with PIDsForeground vs background. A foreground process is attached to your terminal’s controlling process group — it receives keyboard signals (Ctrl-C sends SIGINT, Ctrl-Z sends SIGTSTP) and blocks your prompt. A background process has been placed in a different process group and does not block the prompt.
In ps aux output, STAT ending in + signals a foreground process. STAT without + (and TTY set) is a background job. STAT with ? in TTY is a daemon with no terminal at all.
This distinction matters because: signals typed at the keyboard go only to the foreground process group. To interact with a background process you must address it by PID or use job-control commands (lesson 03).
/proc/<PID> gives you raw kernel data for any live process. Every PID gets a directory in the virtual /proc filesystem:
cat /proc/1843/status # state, memory, UID, GID
cat /proc/1843/cmdline | tr '\0' ' ' # full command line (NUL-delimited)
ls -la /proc/1843/fd # open file descriptors
cat /proc/1843/maps # virtual memory map/proc is not disk — it is a kernel interface that generates content on read. It is the source of truth that tools like ps, top, and lsof read from. When you need something ps does not expose, go directly to /proc.
Find a memory hog on a production server.
ps aux --sort=-%mem | head -10Output (trimmed):
USER PID %CPU %MEM RSS COMMAND
postgres 3412 2.1 18.4 1.5G postgres: autovacuum worker
node 4102 0.8 12.3 1.0G node /app/server.js
redis 1234 0.1 6.2 512M redis-server *:6379PostgreSQL’s autovacuum worker is using 18% of RAM. Next step: check /proc/3412/status for detailed memory breakdown and ps -ef | grep 3412 to confirm it is a child of the main postgres process (expected), not something unexpected.
# Confirm parentage
ps -ef | awk '$2==3412 || $3==3412'▸Why this works
On macOS and BSDs there is no /proc filesystem. ps on macOS uses BSD flags (ps aux works, but -ef behaves differently — ps -ef on macOS shows only your own processes by default). pstree is not installed by default (install via Homebrew: brew install pstree). For production Linux work, these differences do not matter; the tools above work identically on any modern Linux system.
▸Common mistake
%CPU in ps aux is not instantaneous CPU usage — it is the percentage of CPU time consumed over the lifetime of the process. A process that ran flat-out 10 minutes ago but is now idle will still show a high %CPU. For real-time CPU usage, use top or htop (lesson 04), which show a recent rolling average.
A process shows STAT 'Z' in ps aux output. What does this mean?
Every process has a PID and a PPID forming a tree rooted at PID 1. ps aux gives a per-process snapshot with memory, CPU, and state columns; ps -ef adds the PPID column for parent tracing. Key STAT codes: R running, S sleeping, D uninterruptible, T stopped, Z zombie. A foreground process is in the terminal’s active process group (STAT ends in +); a background process is in a different group. /proc/<PID> exposes raw kernel data that all monitoring tools read from.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.