16.1 ps aux: Listing Running Processes and Reading the Output
Right, let’s talk about ps aux. This is the command you’ll run when you ask yourself, “What in the name of all that is holy is actually running on this machine right now?” It’s the first step in diagnosing everything from “why is my fan screaming” to “why is this server on fire.” It’s a foundational tool, and learning to read its slightly arcane output is a superpower.
Let’s break it down because ps aux itself is a bit of a historical mess. The ps command has two major syntax traditions: the BSD style (which is what aux is) and the System V style. Mixing them is a recipe for frustration. The aux flags are a beautiful, if confusing, amalgam:
a: Show processes from all users (not just your own). Thank goodness.u: Show user-focused output (this gives us the nice, readable columns).x: Also show processes not attached to a terminal. This is crucial because, on any modern system, the vast majority of important processes (daemons, your desktop environment, etc.) aren’t tied to a little text terminal.
Run it. Just open a terminal and type ps aux. Go on, I’ll wait.
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169884 13172 ? Ss May31 0:39 /sbin/init splash
root 2 0.0 0.0 0 0 ? S May31 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< May31 0:00 [rcu_gp]
...
chris 1234 0.5 1.2 2123456 98765 tty2 Sl+ 09:25 5:23 /usr/lib/firefox/firefox
...
See? A glorious firehose of information. Let’s make sense of the deluge.
The Output Columns: A Field Guide
Most of these columns are straightforward, but a few are famously misunderstood.
USER: The user who owns the process. Simple. If you see
www-datarunning a shell, you’ve got a problem.PID: The Process ID. The unique number the kernel uses to track this process. You’ll need this to send signals (like
kill -9 1234).%CPU and %MEM: CPU and RAM usage, as a percentage of the total system resources. These are running averages, not instantaneous snapshots. A process showing 99% CPU isn’t necessarily pegging a core right this second; it might have done so a moment ago.
VSZ: Virtual Memory Size. The total amount of virtual memory the process has requested. It’s a big number because it includes shared libraries and memory it asked for but hasn’t used yet. It’s mostly interesting for its… lack of interestingness.
RSS: Resident Set Size. This is the non-swapped physical memory the process is actually using right now. This is the number you care about when asking “what’s eating my RAM?” Note: It counts memory shared between processes multiple times, so the sum of all RSS will be larger than your total RAM. It’s a lie, but a useful one.
TTY: The terminal associated with the process. A
?means it’s a daemon with no terminal.tty2is likely a graphical login session.pts/0is your current terminal window.STAT: The process state code. This is the most important and most cryptic column. It’s a multi-character code that tells you what the process is doing.
S: Interruptible sleep (waiting for an event to finish)R: Running or runnable (on the CPU or in the run queue)D: Uninterruptible sleep (usually waiting on I/O, can’t be killed – a necessary evil)Z: A zombie. The process is dead, but its parent hasn’t “reaped” it. See below.T: Stopped by a job control signal (e.g., by pressingCtrl+Z)- Additional modifiers can include:
<: High priority (not a nice process)N: Low priority (a nice process)s: Session leader (it’s in charge of a group of processes)+: Part of the foreground process group
START and TIME: When the process started and the total cumulative CPU time it has used. A process with a high TIME but low %CPU is old and has done a lot of work in its life.
COMMAND: The command that started the process, with all its arguments. If it’s truncated, you can use
ps auxww(wwfor “wide, wider”) to see the whole thing, which is vital for debugging.
The Zombie in the Room
Let’s talk about that Z state. A zombie process isn’t sucking your CPU or RAM. It’s literally just a corpse—an entry in the kernel’s process table waiting for its parent process to ask, “Hey, how did that process I started die?” using the wait() system call. Until the parent does that, the kernel can’t fully remove the process entry.
You can’t kill a zombie with kill -9; it’s already dead. The problem is the parent process that’s neglecting its duties. To fix a zombie, you usually have to kill its parent, which will then (hopefully) reap its children as it exits. If the parent is init (PID 1), it’s generally good about reaping, and the zombie will vanish on its own. Seeing a zombie for a short time is normal. Seeing one for hours means a buggy parent.
Grepping the Firehose and Other Pro-Tips
The raw output of ps aux is overwhelming. You will almost always pipe it to grep to find what you’re looking for.
# Looking for anything related to nginx?
ps aux | grep nginx
# Is that Java process using too much memory? Let's sort by RSS.
ps aux --sort=-rss | head -10
# Want to see every process owned by the user 'chris'?
ps -u chris
A word of warning: ps aux | grep [n]ginx is a classic trick (the brackets mean the grep command itself won’t match the pattern [n]ginx), but pgrep is the more robust, built-in tool for this job.
# A better way to find PIDs for a process name
pgrep -l nginx
The designers of ps gave us a thousand flags and options because they couldn’t agree on one standard. It’s a mess, but it’s our mess. ps aux is the go-to for a reason: it gives you the most immediately useful information with a simple, memorable incantation. Learn to read its output like a street sign, and you’ll never be truly lost in your system again.