Guides

ps aux explained: every column, plus the ps commands you actually need

What USER, PID, %CPU, %MEM, VSZ, RSS, STAT and TIME mean in ps aux, how to sort by memory or CPU, find a process by name and show the process tree.

Published ~9 min read

ps aux lists every process on the system, one per line. The three letters are options: a shows processes of all users, u adds user-oriented columns, and x includes processes without a terminal, such as services and daemons. The columns that matter most are %CPU, %MEM, RSS and STAT. Keep in mind that %CPU is an average over the process’s whole lifetime, not what it is using right now; for that, use top.

ps aux · example on a 4 GB server
$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.2 168160  9548 ?        Ss   Nov02   0:41 /sbin/init
root           2  0.0  0.0      0     0 ?        S    Nov02   0:00 [kthreadd]
mysql        812  1.4 11.2 2486532 457812 ?      Ssl  Nov02  52:10 /usr/sbin/mariadbd
www-data    4182  2.1  4.8 412516 196204 ?       S    09:14   1:03 php-fpm: pool www
deploy      5120  0.0  0.1  10072  5412 pts/0    Ss   10:02   0:00 -bash
deploy      5301  0.0  0.1  11284  4600 pts/0    R+   10:05   0:00 ps aux

What does each column in ps aux mean?

  • USER: the account the process runs as. Names longer than eight characters are cut off and end in +, as in systemd+.
  • PID: the process ID, which you pass to kill, renice or ps -p.
  • %CPU: CPU time used divided by the time the process has been running. That is a lifetime average: a process that was busy for an hour and is idle now still shows a high value, and a sudden spike in a long-running process barely moves it. One full core is 100%, so multi-threaded programs can go above 100%.
  • %MEM: RSS as a percentage of the machine’s physical RAM.
  • VSZ: virtual memory size in KiB. This is all the address space the process has mapped, including memory it reserved but never used. It is mostly irrelevant; Java, Go and database processes routinely show huge values.
  • RSS: resident set size in KiB, the part of the process that actually sits in RAM. This is the memory figure to look at. Shared libraries are counted in every process that uses them, so adding up the RSS of all processes gives more than the memory actually in use.
  • TTY: the terminal the process is attached to. ? means none (services), and pts/0 is an SSH or terminal session.
  • STAT: the process state plus modifiers, explained below.
  • START: when the process started. A time such as 09:14 means within the last 24 hours, a date such as Nov02 means earlier, and a bare year means more than a year ago.
  • TIME: the total CPU time consumed, in minutes and seconds (52:10 is 52 minutes of CPU), not how long the process has existed.
  • COMMAND: the command line with its arguments. Names in square brackets such as [kthreadd] are kernel threads. In a terminal, long lines are cut at the screen edge; ps auxww shows them in full.

What do the STAT codes mean?

The first letter is the state:

  • R running or waiting for a CPU, S sleeping until something happens (most processes, most of the time), I an idle kernel thread.
  • D uninterruptible sleep, usually waiting for disk or network storage.
  • Z zombie: finished, but not yet collected by its parent.
  • T stopped, for example by Ctrl+Z; t stopped by a debugger.

Then come the modifiers: s session leader, l multi-threaded, < high priority, N low priority (niced), L pages locked in memory, and + in the foreground of its terminal. So Ssl is a sleeping, multi-threaded session leader (a typical daemon), and R+ is the command you are running right now.

How do I sort ps aux by memory or CPU?

sort
ps aux --sort=-%mem | head -n 11     # the 10 largest memory users (plus the header)
ps aux --sort=-rss  | head -n 11     # the same, by resident memory in KiB
ps aux --sort=-%cpu | head -n 11     # the 10 highest lifetime CPU averages

The minus sign means descending. Because %CPU is a lifetime average, the CPU sort shows long-term consumers, not what is pegging the CPU at this moment. For that, run top and press P (CPU) or M (memory), or use pidstat 1 5 from the sysstat package.

How do I choose my own columns?

-o takes a list of column keywords. Combined with -e (every process), it gives you exactly the view you need:

custom output
ps -eo pid,ppid,user,%cpu,%mem,rss,etime,args --sort=-%mem | head
ps -o pid,etime,lstart,args -p 1234     # one process: running time and exact start
ps -o rss= -p 1234                      # just the number; '=' hides the header

# memory in MiB instead of KiB
ps -eo pid,rss,comm --sort=-rss | awk 'NR==1{print;next}{printf "%7d %8.1f MiB  %s\n",$1,$2/1024,$3}' | head

Useful keywords: pid, ppid (parent), user, %cpu, %mem, rss, vsz, stat, etime (elapsed time since start), etimes (the same in seconds), lstart (full start date), comm (name only), args (full command line), nlwp (number of threads), ni (nice value) and wchan (the kernel function a sleeping process waits in).

How do I find a process by name?

find
pgrep -a nginx                # PIDs plus full command lines
pgrep -af 'node server.js'    # match against the whole command line
pgrep -u www-data -a          # everything one user is running
pidof nginx                   # just the PIDs of an exact program name
ps -fp $(pgrep -d, nginx)     # full ps details for every match
ps aux | grep '[n]ginx'       # the classic, without grep listing itself

A plain ps aux | grep nginx also shows the grep nginx process itself. The pattern [n]ginx still matches “nginx”, but grep’s own command line now contains “[n]ginx”, which the pattern does not match. pgrep skips itself anyway and is easier to use in scripts.

How do I see the process tree?

tree views
ps auxf                       # ps aux with the tree drawn in the COMMAND column
ps -ef --forest               # the same in -ef format, with parent PIDs
pstree -p                     # compact tree with PIDs (package psmisc)
pstree -s -p 1234             # the parents of PID 1234, up to PID 1
systemctl status nginx        # a service's own processes, from systemd

The tree answers “who started this?”, for example which service a mysterious worker belongs to, or whether a process runs under cron, a shell or a container. If pstree is missing, install psmisc.

ps aux or ps -ef: which should I use?

Both list every process. ps aux uses BSD syntax (options without a dash) and shows resources: %CPU, %MEM, VSZ, RSS and STAT. ps -ef uses UNIX syntax (options with a dash) and shows UID PID PPID C STIME TTY TIME CMD, including the parent PID that ps aux lacks. Use ps aux to look at resource usage, and ps -ef to see who started what.

ps -aux with a dash usually works, but only by accident. Strictly, it asks for the processes of a user named “x”, and ps falls back to ps aux only if no such user exists. Write it without the dash.

How do I see threads?

threads
ps -eLf | head               # every thread: LWP is the thread ID, NLWP the count
ps -T -p 1234                # the threads of one process (SPID = thread ID)
ps -o nlwp= -p 1234          # just the number of threads
top -H -p 1234               # live, per thread

Threads matter when one process uses a lot of CPU and you need to know which part of it. top -H shows which thread is busy, and many programs name their threads, so the name often tells you.

What are D-state and zombie processes?

D: uninterruptible sleep

A process in state D is waiting inside the kernel, usually for disk or network storage, and cannot be interrupted until that I/O returns. Not even kill -9 works before then. On Linux, D-state processes count towards the load average, which is why a server can show a high load while the CPU sits idle. Many processes stuck in D point at slow or overloaded storage; see how to test disk speed.

who is stuck in D?
ps -eo pid,stat,wchan:32,args | awk '$2 ~ /^D/'

Z: zombies

A zombie has already exited. All that is left is its entry in the process table, waiting for the parent to read the exit status. It uses no CPU and no memory, and you cannot kill it, because it is already dead. The fix is the parent: restart it, or fix its code so it collects its children. When the parent exits, PID 1 adopts the zombie and removes it. A few zombies are harmless; thousands mean a bug in the parent and can eventually exhaust the available PIDs.

find zombies and their parents
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
ps -o pid,args -p <PPID>      # the parent that is not collecting them

How do I stop a process?

stop
kill 1234                     # SIGTERM: ask it to exit cleanly
kill -9 1234                  # SIGKILL: last resort, no cleanup
pkill -f 'python3 bot.py'     # by command line; check the match with pgrep -af first
sudo systemctl stop nginx     # a service: stop the unit, or systemd starts it again

Always try plain kill first. It lets the program close files and finish writes. kill -9 cannot be caught, so a database killed that way may need recovery on its next start. For anything managed by systemd, use systemctl stop; otherwise the service manager restarts what you just killed.

What do I do with what ps shows?

  • Something uses a lot of CPU: confirm it with top, then follow VPS high CPU usage. It also covers how to tell a cryptominer from a legitimate process.
  • Memory is running out or processes disappear: sort by RSS, then read why Linux kills processes when memory runs out.
  • ps itself says “command not found”: the procps package is missing, which is common in containers. See ps: command not found. On Alpine, ps is BusyBox’s cut-down version, which ignores most of the options on this page; apk add procps installs the full one.

On a KVM VPS, ps reads your own kernel’s process table. PID 1 is your own systemd, every line is a process on your machine, and %MEM is relative to the RAM assigned to your VM. On container-based virtualization, processes run on a kernel shared with the host, and some figures describe the host rather than your container; KVM vs OpenVZ explains the difference. Every VMHeaven KVM plan is a full virtual machine of that kind.

What is the Windows equivalent of ps aux?

cmd · PowerShell
tasklist                                        # cmd: name, PID, memory
tasklist /v /fi "imagename eq w3wp.exe"         # details for one program
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 Name, Id, CPU, WS
Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId, CommandLine

Frequently asked

What is the difference between VSZ and RSS in ps?

VSZ is the virtual memory size: all the address space a process has mapped, including memory it reserved but never touched. RSS is the resident set size: the part that is actually in RAM right now. Both are in KiB. RSS is the figure that matters for 'what is using my memory', while VSZ is routinely huge for Java, Go and database processes and is rarely worth worrying about.

What is a zombie process?

A zombie (state Z in ps) is a process that has already exited but whose parent has not yet read its exit status. It uses no CPU or memory, only a slot in the process table, and kill -9 cannot remove it because it is already dead. Find the parent with 'ps -o ppid= -p <PID>' and restart or fix that parent. Once the parent exits, PID 1 adopts and removes its zombies.

Why can %CPU in ps be above 100%?

ps counts one fully used CPU core as 100% and adds up all threads of a process. A multi-threaded program keeping four cores busy can therefore show close to 400%. Remember that ps averages %CPU over the process's whole lifetime; top shows the current figure.

How do I see the full command line of a process?

In a terminal, ps aux cuts long lines at the screen edge. Use 'ps auxww' for unlimited width, or 'ps -o args= -p <PID>' for one process. The raw source is /proc/<PID>/cmdline, with arguments separated by null bytes: 'tr "\0" " " < /proc/<PID>/cmdline; echo' prints it readably. 'pgrep -af <pattern>' finds processes by name and prints their full command lines in one step.

Related articles

All guides