Processes & Jobs
Process states, PID/PPID, fork/exec, foreground vs background jobs, signals, and the tools to inspect and control running processes.
What Is a Process?
A process is a running instance of a program — it has its own PID (process ID), memory space, open file descriptors, and a PPID (parent process ID) tracing back to systemd/init (PID 1).
Process Creation: fork() and exec()
Every process except PID 1 is created by another process calling fork() (duplicate the calling process) followed by exec() (replace that copy’s memory with a new program).
ps -ef --forest # process tree with parent/child relationships
pstree -p # visual tree with PIDs
ps -eo pid,ppid,cmd | head
Process States
| State | Meaning |
|---|---|
R Running | Actively executing or ready to run on a CPU |
S Sleeping | Waiting on an event (I/O, timer) — interruptible |
D Uninterruptible sleep | Waiting on I/O that can’t be interrupted (often disk) — high D-state counts indicate storage trouble |
Z Zombie | Finished executing, but parent hasn’t yet called wait() to read its exit status |
T Stopped | Suspended (e.g., via Ctrl+Z or SIGSTOP) |
ps aux | awk '{print $8}' | sort | uniq -c # count processes by state
Foreground vs Background Jobs
sleep 300 & # start in background, returns shell immediately
jobs # list background jobs for this shell
fg %1 # bring job 1 to foreground
bg %1 # resume a stopped job in the background
Ctrl+Z # suspend the foreground job (state becomes T)
disown -h %1 # detach job so it survives the shell exiting
nohup long_task.sh & # ignore SIGHUP so the process survives logout
Signals: How Processes Are Told to Do Something
kill -l # list all signals
kill -15 1234 # SIGTERM: ask process 1234 to shut down gracefully (default)
kill -9 1234 # SIGKILL: force-kill immediately, cannot be caught/ignored
kill -1 1234 # SIGHUP: often used to tell a daemon to reload config
kill -STOP 1234 / kill -CONT 1234 # pause / resume
pkill -f "python worker.py" # kill by matching command line
| Signal | Number | Meaning | Can be caught? |
|---|---|---|---|
| SIGHUP | 1 | Hangup / reload | Yes |
| SIGINT | 2 | Interrupt (Ctrl+C) | Yes |
| SIGTERM | 15 | Polite request to terminate | Yes (graceful shutdown) |
| SIGKILL | 9 | Force kill | No — kernel terminates immediately |
| SIGSTOP | 19 | Pause | No |
Zombies vs Orphans
- A zombie process has exited but its exit status hasn’t been reaped by its parent — it holds only a PID table entry, not real resources, but too many indicate a parent that isn’t calling
wait(). - An orphan process’s parent died first — it gets re-parented to PID 1 (
systemd/init), which reaps it automatically.
ps aux | grep 'Z' # find zombie processes
Production Considerations
- Always try
SIGTERMbeforeSIGKILL—SIGKILLgives the process no chance to flush buffers, close DB connections, or clean up temp files. - Persistent
D-state processes are a strong signal of disk/NFS problems, not application bugs —kill -9won’t even work on them. - In containers, PID 1 doesn’t get default signal handling like a real init does — use
tiniordumb-init(or--initin Docker) soSIGTERMand zombie reaping work correctly.
Quick Interview Answer
“A process is created via fork()+exec() and tracked by PID/PPID, with states like Running, Sleeping, uninterruptible-Sleep (D), Zombie, and Stopped. Signals are how you communicate with processes — SIGTERM (15) asks for graceful shutdown and can be caught, SIGKILL (9) forces immediate termination and can’t be caught. A zombie has exited but not been reaped by its parent; an orphan gets re-parented to PID 1.”
Common Mistakes
- Reaching for
kill -9by default instead ofSIGTERMfirst, skipping graceful shutdown. - Running a container without a real init process, leading to zombie accumulation and signals not propagating correctly.
- Confusing zombie (exited, not reaped) with orphan (parent died, re-parented to init) in an interview.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form