How-to

Linux Processes: ps, top, htop, kill, Signals and nice

Updated: · Skillaria.Top

Linux processes are inspected with ps aux (a one-time snapshot) and with top or htop (a live list with load). A process is stopped with kill PID, which by default sends a polite SIGTERM; kill -9 (SIGKILL) is needed only when the process does not respond. Priority is set with nice when starting a program and with renice for a running process, in the range from -20 to 19.

Every running program in Linux is a process with its own PID number, owner, state and priority. You can view processes with ps, top and htop, stop them with kill, and change their priority with nice and renice. Below we go through how to read the output of these commands, which signals to send and when, and why kill -9 should not be the first reflex.

What a process and a PID are

A process is a program while it is running: its code, memory, open files and a set of attributes. The kernel gives each process a unique identifier, the PID, and also records the PPID — the number of the parent that started it. The very first process in the system, with PID 1, is usually systemd, and it becomes the ancestor of all the others.

A new process appears in two steps. First the parent calls fork() and gets its own copy, then the copy calls execve() and replaces its code with the code of the new program. When you type ls in the terminal, bash does exactly this and waits for the child to finish through wait().

$ echo $$          # PID of the current shell
4182
$ ps -o pid,ppid,cmd -p $$
    PID    PPID CMD
   4182    4170 -bash

Viewing processes with ps

ps takes a one-time snapshot: it shows the processes exactly at the moment it runs and then exits. Without arguments it shows only the processes of the current terminal, so in practice one of two variants is almost always used.

$ ps aux | head -5
USER       PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND
root         1  0.0  0.3 168540 12780 ?     Ss   09:12   0:03 /sbin/init
root         2  0.0  0.0      0     0 ?     S    09:12   0:00 [kthreadd]
www-data  1532  1.2  2.1 412308 86420 ?     S    09:15   1:47 php-fpm: pool www
postgres  1610  0.4  3.8 221004 154312 ?    Ss   09:15   0:31 postgres: checkpointer

Here a means processes of all users, u the detailed format with the owner, and x includes processes without a terminal (daemons). The System V-style alternative is ps -ef: it shows the PPID, which is handy when you need to find out who started whom.

ColumnWhat it means
PIDprocess identifier
%CPU, %MEMshare of the processor and physical memory
VSZvirtual memory in KB (including what is not loaded into RAM)
RSSphysical memory actually in use, in KB
STATprocess state (R, S, D, Z, T) and flags
TIMEtotal CPU time, not the running time

Useful ps tricks

# top 10 by memory
ps aux --sort=-%mem | head -11

# top 10 by CPU
ps aux --sort=-%cpu | head -11

# custom columns
ps -eo pid,ppid,user,ni,stat,etime,cmd

# process tree
ps -ef --forest
pstree -p

# find a PID by name
pgrep -a nginx
pidof nginx

A classic mistake is searching for a process with ps aux | grep nginx and getting the grep itself in the output. Use pgrep or the trick grep [n]ginx, which does not match its own command line.

Process states: R, S, D, Z, T

The letter in the STAT column is not noise but the real state of the process. It determines how to interpret the load and whether kill can help at all.

CodeStateWhat it means in practice
RRunningexecuting or waiting in the CPU run queue
SInterruptible sleepwaiting for an event (network, input, timer); the usual state of most processes
DUninterruptible sleepwaiting for I/O inside the kernel, signals are not delivered
ZZombiefinished, but the parent has not collected its exit code
TStoppedsuspended by the SIGSTOP signal or Ctrl+Z

Extra symbols after the letter are useful too: s — session leader, + — foreground process in the terminal, < — raised priority, N — lowered priority, l — multithreaded.

A process in state D

A process in D is inside a system call and waits, for example, for a reply from a disk or a hung NFS share. It does not receive any signal, not even SIGKILL. That is why kill -9 is useless here: you need to find the cause of the wait — check iostat, dmesg and the state of the network storage.

Zombie processes

A zombie is already dead: it uses no CPU and no memory, and only occupies a line in the process table. It cannot be killed — it has already finished. It disappears when the parent calls wait(), or when the parent itself exits and the zombie is "adopted" by PID 1.

# find zombies and their parents
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'

A single zombie is harmless. If there are hundreds of them and the number keeps growing, the parent program has a bug: fix it or restart it.

top: processes and load in real time

top shows the same list but refreshes it every few seconds and displays a system summary. It is the first command people run when "the server is slow".

top - 14:21:07 up 12 days,  3:02,  2 users,  load average: 1.84, 1.20, 0.95
Tasks: 213 total,   2 running, 211 sleeping,   0 stopped,   0 zombie
%Cpu(s): 23.1 us,  4.2 sy,  0.0 ni, 70.3 id,  1.9 wa,  0.0 hi,  0.5 si,  0.0 st
MiB Mem :   7821.4 total,    412.6 free,   3105.8 used,   4303.0 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   4398.2 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   1532 www-data  20   0  412308  86420  14212 R  38.2   1.1   1:47.31 php-fpm
   1610 postgres  20   0  221004 154312 148900 S   6.0   1.9   0:31.08 postgres

The %Cpu(s) line matters most: us — user code, sy — kernel, id — idle, wa — waiting for disk. A high wa with an idle processor is a clear sign that the bottleneck is I/O.

top hotkeys

KeyAction
P / Msort by CPU / by memory
1show each CPU core separately
cfull command line
ufilter by user
ksend a signal to a process
rchange a process's nice value
Hshow threads
qquit

For scripts and logs the batch mode is useful: top -b -n 1 | head -20 prints one snapshot without interaction. To watch a specific process, use top -p 1532.

Load average is a queue, not a percentage

The three numbers of load average are the average queue length over 1, 5 and 15 minutes. In Linux, processes in states R and D go into it, but ordinary sleep S does not. To judge LA, compare it with the number of cores (nproc): on 4 cores a value of 4 means full load, and 12 means a queue three times larger than the capacity.

If LA is high while the processor is idle, look for processes in D: ps -eo stat,pid,cmd | grep '^D'. It is almost always a slow disk or network storage.

htop: a convenient replacement for top

htop shows the same information but with colored per-core bars, scrolling, mouse support and a process tree. It usually has to be installed first: sudo apt install htop or sudo dnf install htop.

  • F3 or / — search for a process by name;
  • F4 — filter that shows only matches;
  • F5 or t — "parent — children" tree;
  • F6 — choose the sort column;
  • F7 / F8 — decrease or increase nice;
  • F9 — signal menu for the selected process;
  • Space — mark several processes for a bulk action.

On servers where you cannot install anything, top is enough: all the key operations are available there too.

How to stop a process: kill and signals

The kill command does not really "kill" anything; it sends a signal to the process. What happens next depends on the signal and on how the program handles it.

SignalNumberPurpose
SIGHUP1terminal hangup; many daemons reload their config
SIGINT2interrupt, the same as Ctrl+C
SIGKILL9forced termination by the kernel, cannot be caught
SIGTERM15a request to exit gracefully; the default for kill
SIGSTOP19suspend, cannot be caught
SIGCONT18resume a suspended process
SIGTSTP20suspend from the terminal, Ctrl+Z

Numbers are given for x86 and ARM; the full list is printed by kill -l. Naming the signal is more reliable than using its number.

kill 1532              # SIGTERM
kill -TERM 1532        # the same, explicitly
kill -HUP $(pidof nginx)   # reload the configuration
kill -STOP 1532; kill -CONT 1532
kill -0 1532 && echo "process is alive"

Signal 0 sends nothing; it only checks whether the process exists and whether you have the right to signal it — handy in scripts.

Why not start with kill -9

SIGTERM gives the program a chance to close files, flush buffers, remove lock files and complete transactions correctly. SIGKILL cuts everything off instantly: unwritten data is lost, temporary files remain, and a database may go into recovery on the next start.

The right sequence is:

  1. Send kill PID (SIGTERM).
  2. Wait 5–30 seconds, checking with kill -0 PID.
  3. Only if the process is still alive and not in state D — kill -9 PID.

For services under systemd it is better not to touch PIDs manually at all: systemctl stop sends SIGTERM itself, waits for the timeout and then finishes with SIGKILL.

pkill and killall: by name

pkill -f "python worker.py"   # by the full command line
pkill -u alice                 # all processes of a user
killall -TERM node             # all processes named node
pgrep -l -f worker             # check first what the pattern will match

Before a mass pkill, always run pgrep with the same pattern: -f matches any part of the command line and can easily catch unrelated processes.

You can send signals only to your own processes; for others you need sudo. The error Operation not permitted means exactly that.

Process priority: nice and renice

The nice value tells the scheduler who should yield the processor. The range is from -20 (the highest priority) to 19 (yields to everyone), the default is 0. In top it is shown in the NI column, while PR is the derived internal priority.

# start a heavy job with lowered priority
nice -n 10 tar czf backup.tar.gz /var/www

# lower the priority of a running process
renice -n 15 -p 1532

# raise it (root only)
sudo renice -n -5 -p 1610

# all processes of a user
sudo renice -n 10 -u alice

An ordinary user can only lower the priority of their own processes; negative values are available only to root. It is important to understand the limits of nice: it affects only the CPU. If a task slows the system through the disk, use ionice -c3 command — the idle class, which gets the disk only when nobody else needs it.

Background processes and shell jobs

A command can be started in the background by adding &, or suspended with Ctrl+Z. The shell keeps a list of jobs, which are managed with built-in commands.

$ long_task &
[1] 5120
$ jobs
[1]+  Running    long_task &
$ fg %1        # bring to the foreground
^Z             # suspend (SIGTSTP)
$ bg %1        # continue in the background

When the terminal is closed, the shell sends SIGHUP to its jobs, and they exit. To let a process survive logout, start it with nohup command &, detach it with the disown command, or use tmux. For permanent work it is better to set it up as a systemd service.

Typical diagnostic scenarios

Who is eating the CPU

top -o %CPU            # or P inside top
ps -eo pid,user,%cpu,etime,cmd --sort=-%cpu | head

If a process consumes CPU without any benefit, first lower its priority with renice — this is safe and gives you time to investigate.

Who has taken the port

sudo ss -ltnp | grep :8080
sudo lsof -i :8080

The output contains the PID, and from it you can find the executable (exe), the working directory (cwd) and the arguments (cmdline) in /proc/PID/.

The process disappeared unexpectedly

A common cause is the OOM Killer, which kills processes when memory runs out. Check with dmesg -T | grep -i oom or journalctl -k | grep -i "killed process".

Summary

A small set of commands is enough for working with Linux processes: ps aux for a snapshot, top and htop for monitoring, kill with the right signal for stopping, and nice/renice for priorities. Read the state of a process before acting: zombies and processes in D cannot be cured with signals. Start with SIGTERM and keep kill -9 for the last resort. Processes, memory, load average and system calls are covered in detail with practice in the Linux course.

FAQ

How is ps aux different from ps -ef?

These are two syntaxes of the same command: aux is the BSD style with the %CPU, %MEM and STAT columns, -ef is the System V style with the PPID column. Both show the same set of processes — all processes in the system.

How do I find out how many processes are running in the system?

The easiest way is to look at the Tasks line in top, or run ps -e --no-headers | wc -l. The number of threads is shown by ps -eLf --no-headers | wc -l.

Why does a process not stop even after kill -9?

Either it is in state D and waits for I/O inside the kernel, or it is a zombie that has already finished. In the first case look for a problem with the disk or network storage; in the second, fix or restart the parent.

What happens if I kill the process with PID 1?

The kernel protects init: ordinary signals, including SIGKILL from a user, are not delivered to it unless it handles them. If init does exit, the system goes into a kernel panic.

How do I limit a process's memory usage?

For a systemd service set MemoryMax= in the unit file; for a one-off run use systemd-run --scope -p MemoryMax=500M command. This works through cgroups and is more reliable than the old ulimit -v.

Learn this topic on video

This topic is covered in depth with practice in the course “Linux: с нуля до уверенного администрирования”.

Go to course