Every running program is a process living in its own ecosystem. This episode dissects PID/PPID and process states, practices monitoring with ps, top, and htop, controls processes through signals, and runs background tasks with nohup, screen, and tmux.

After episode 12 where we covered the shell, environment variables, and .bashrc customization — from VAR=x vs export VAR, startup files, to your first bash script — you can now give complex instructions to the system. But there's something running behind the scenes every time you type a command: processes. Every program, from the simplest ls to an Nginx serving thousands of requests, is a process living in the kernel, consuming CPU and RAM, and can be seen, monitored, and controlled.
In this episode, we'll open up Linux's "engine room": what PID and PPID are, what process states like R, S, and Z (zombie) mean, how to read a snapshot with ps, monitor real-time with top/htop, and how to stop processes with the right signals. By the end of the episode, you'll know exactly what happens when a server "hangs" and how to handle it without panicking. Let's begin.
When the kernel runs a program, it creates a process — an entity with its own program code, data, and execution context. Every process is given a PID (Process ID), a unique number within the system. The process that created it is called the parent, with a PPID (Parent Process ID). This is why all processes in Linux form a tree: there's one root at number 1, and every process is a branch of another process.
Imagine a highly organized restaurant. The head chef (PID 1, systemd or init) hires kitchen staff. Any staff member who recruits another is recorded as the parent of their recruit. If a staff member suddenly quits, their recruits become "orphaned" and are adopted by the head chef. This analogy explains why every orphaned process is eventually reparented to PID 1.
Every process also has a state reflecting what it's currently doing. The four states you'll see most often:
| State | Meaning | When it happens |
|---|---|---|
R (Running) | The process is actively executing code | Currently using CPU |
S (Sleeping) | The process is waiting for something (I/O, timer) | Waiting on disk, network, or events |
D (Uninterruptible sleep) | Waiting for I/O that can't be interrupted | Waiting on a problematic disk (hard to kill) |
Z (Zombie) | The process is dead but not yet "buried" by its parent | The parent hasn't called wait() yet |
State Z is one of the phenomena that most often panics beginner admins. A zombie is a process that has finished its job but whose entry hasn't been cleaned from the process table, because its parent hasn't called wait(). Zombies don't consume CPU or RAM — they're just a leftover identity. They pile up only if the parent is buggy (never calls wait) or the parent itself is stuck.
ps, pstreeThe main command for taking a process snapshot is ps. The two most used syntaxes: ps aux (BSD format) and ps -ef (POSIX format). Both display a full process list; the difference is just output style and column names.
ps auxThe ps aux output you'd typically see (truncated here):
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 167520 11544 ? Ss Aug01 0:15 /sbin/init
arman 12345 2.5 3.1 620000 128000 ? Ssl Aug01 12:04 node server.js
nginx 23456 0.1 0.2 210000 20000 ? S Aug01 0:30 nginx: worker process
arman 34567 0.0 0.0 0 0 ? Z Aug01 0:00 [defunct]Some columns you must recognize:
kill uses.S sleeping, R running, Z zombie).Notice the fourth row: STAT Z and the command name [defunct] are the classic signs of a zombie process. If you see it, no need to panic — it's just waiting for its parent to call wait(). If they pile up, check the parent, not the zombie itself.
Meanwhile, pstree presents processes as a family tree, exactly like the restaurant genealogy earlier:
pstreesystemd─┬─nginx───2*[nginx]
├─sshd───sshd───bash───node───4*[node]
└─systemd-journaltop and htopps is a photo; top is a video camera. top refreshes its display every few seconds, sorts processes by CPU usage, and shows a system summary at the top — load average, process count, CPU usage per component, and memory usage. Run it with top then press q to quit.
top
# Install htop — an interactive display with mouse & colors
sudo apt install htop
htophtop is a friendlier replacement for top: colorful, scrollable, mouse support, and a process killer command (F9) without needing to remember PIDs. For production servers, top remains the standard because it's always available; htop is an added convenience. Both monitor the same thing: who's consuming the most resources right now.
Tip
In top, press M to sort by memory usage and P to return to CPU ordering — a quick way to find "naughty" processes. To see processes of a specific user, run top -u arman. The habit of reading top output whenever a server starts slowing down is a skill that will pay off hugely in episode 24 (performance tuning).
This is the most important part. Linux doesn't "kill" processes haphazardly — it sends signals, and the process decides how to respond. Signals are the standard inter-process communication method; you send them via kill <PID>, killall, or pkill. Some signals you must memorize:
| Signal | Number | Default behavior | Usage |
|---|---|---|---|
SIGTERM | 15 | The process is politely asked to stop | Stopping a process normally |
SIGKILL | 9 | Forced to stop, cannot be refused | A completely stuck process that won't die |
SIGHUP | 1 | Hang up — often used to reload config | nginx reload without restart |
SIGINT | 2 | Interrupt — same as Ctrl+C | Canceling a foreground process |
SIGSTOP | 19 | Process is paused (can be resumed) | Like Ctrl+Z |
# Send SIGTERM (15) — a polite stop request
kill 12345
# Send a specific signal
kill -15 12345 # same as kill 12345
kill -9 12345 # SIGKILL — force stop
kill -1 12345 # SIGHUP — request a config reload
# Kill by name (not PID)
killall node
pkill -f "node server.js"Important
Never go straight to kill -9. That signal cuts a process off roughly without giving it a chance to clean up — files being written can get corrupted, databases can lose data, and in-memory state is lost. The correct order: send SIGTERM (15), wait a few seconds, only then consider SIGKILL (9) if the process is truly stubborn. The analogy: SIGTERM is saying "please stop now", SIGKILL is cutting power straight from the main switch.
pkill -f "pattern" has both power and danger: it kills all processes whose command line matches the pattern. A wrong pattern and you can kill processes you never intended. Use pgrep -f first to see what will match before pkill.
So far all our commands run in the foreground — the terminal is locked until the command finishes. For long-running processes (like a dev server), there's a background mechanism:
# Run directly in the background with the & sign
sleep 300 &
# List the jobs in this shell session
jobs -l
# Move a job to the foreground
fg %1
# Pause a foreground process with Ctrl+Z, then resume it in the background
# (after pressing Ctrl+Z)
bg %1The full map: command & starts a job in the background; Ctrl+Z temporarily stops a foreground job; jobs lists jobs with %n numbers; fg %n returns one to the foreground; bg %n resumes a paused job in the background. Note the important difference: a background job isn't a "detached" process — it's still tied to the session terminal. Close that terminal, and the job dies with it.
Note
Why does a background job die when the terminal closes? Because the kernel sends SIGHUP (hang up) to all processes tied to the terminal when it closes. To detach a process from the terminal, we need a tool that "adopts" the process — that's what nohup, screen, and tmux are for.
nohup, screen, and tmuxFor jobs that must survive an SSH disconnect — database migrations, big builds, or servers that must run 24/7 — there are three levels of solution:
nohup (no hang up) is the simplest: it ignores the SIGHUP signal, so the process survives after the terminal closes. Its output is redirected to nohup.out.
nohup ./migrate-data.sh > migrasi.log 2>&1 &
# The process keeps running; check the results later
tail -f migrasi.logscreen and tmux are terminal multiplexers — they create terminal sessions living separately from the physical terminal. You can detach a session, close SSH, and come back later; the process keeps running and the output is still there. tmux is the modern, most popular standard:
# Create a new session named "deploy"
tmux new -s deploy
# Inside the session: Ctrl+B then D to detach
# (processes inside keep running)
# List all active sessions
tmux ls
# Return to the session
tmux attach -t deployTip
Which one to use when? nohup for a single command that needs to survive. tmux when you need an interactive workspace — running several panes, scrolling output, and returning to the same session after a disconnect. For production servers that must auto-restart on crash, don't use any of the three — that's systemd's job, which will be the topic of episode 14.
| Mistake | Symptom | Solution |
|---|---|---|
kill -9 directly without SIGTERM | Corrupted data, process has no time to clean up | Send SIGTERM first, wait, then SIGKILL |
| Panicking at the sight of zombie processes | Want to kill a zombie (it won't work) | Check the parent; zombies are cleaned up by their parent |
pkill -f with too broad a pattern | Unexpected processes get killed too | pgrep -f first to see the matches |
Killing a PID from ps without verification | Wrong process killed (PID reused) | Verify the name & start time, or use pgrep |
| Closing the terminal with a background job | The job dies (SIGHUP) | Use nohup, screen, or tmux |
kill on a D-state process | Won't die even with -9 | Wait for the I/O to finish; -9 doesn't affect D |
One thing worth remembering: PIDs can be reused by the system. The process you see in ps now could already be dead and its PID taken by another process. That's why pkill/killall (name-based) are safer for "kill them all" and pgrep is useful for verifying before per-PID kill.
In this episode 13 we've opened up Linux's engine room: understanding PID/PPID and process states including zombies, reading snapshots with ps aux and hierarchies with pstree, monitoring real-time with top/htop, controlling processes via the signals SIGTERM/SIGKILL/SIGHUP, and moving work to the background with &, jobs, fg, bg, plus making it persistent with nohup, screen, and tmux. The most important lesson: processes are citizens of the system with rights and protocols — treat them with respect via the right signals, and the system will cooperate with you.
But there's one question hanging since earlier: who keeps important processes always running — even after a crash or reboot? The answer is systemd, the modern init system that's process number 1 on almost every distro. In the next episode 14 we'll discuss systemd and service management: systemctl, journalctl, through to writing your own service unit files. See you there!