Linux Process Management: ps, kill, nice, and systemd

Tested on: Ubuntu 26.04 LTS · Debian 12 · Fedora 44 · Arch Linux — Last updated: June 2026
Every program running on your system is a process. Linux gives you complete visibility and control over those processes — inspect them, adjust their priority, send signals, confine their resources, or tie them to systemd for supervised execution. This guide covers the full toolkit: ps, top, htop, kill, nice, systemctl, strace, lsof, and cgroups.
Prerequisites
- A Linux system running systemd (Ubuntu 24.04+, Debian 12+, Fedora 33+, or equivalent)
- Basic terminal familiarity — you know how to open a shell and run commands
sudoaccess for signal operations on other users' processes and systemd management- Optional:
htop,strace,lsof,smartmontools— install instructions are included inline
What Is a Process
A process is a running instance of a program, complete with its own memory space, file descriptors, and kernel-assigned Process ID (PID). When you type ls, the shell forks a child process, the kernel loads the ls binary into it, executes it, and tears the process down when it exits. Long-running programs — web servers, databases, daemons — persist as processes until explicitly stopped or they crash.
Key concepts you'll see throughout this guide:
- PID: Unique integer assigned by the kernel. Recycled after a process exits.
- PPID: Parent PID. Every process was spawned by another process.
- PID 1: The init system (
systemdon modern distros). First process started by the kernel; parent of all others. - Daemon: A background process with no controlling terminal —
sshd,nginx,cron. - Thread: A lightweight execution unit within a process, sharing the same memory space. The kernel schedules threads independently.
- Zombie: A process that has exited but whose parent hasn't called
wait()to collect its exit status. Shows as stateZ. Harmless in small numbers; a large accumulation indicates a bug in the parent.
ps — Snapshot of Processes
ps prints a point-in-time snapshot of running processes. It reads from the /proc filesystem and exits — it's not live.
# Your own processes only (minimal output):
ps
# All processes, all users, with detailed info — the most common invocation:
ps aux
# a = all users' processes with a terminal
# u = user-oriented format (shows USER, %CPU, %MEM, RSS)
# x = include processes without a controlling terminal (daemons)
# Show full process tree with parent-child relationships:
ps auxf
# Sort by CPU (descending):
ps aux --sort=-%cpu | head -20
# Sort by resident memory (actual RAM in use):
ps aux --sort=-%mem | head -20
# Find a specific process:
ps aux | grep nginx
# Show all threads (useful for diagnosing multi-threaded apps):
ps -eLf | grep javaReading ps Output
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 169012 12348 ? Ss Jun01 0:03 /sbin/init
deploy 1234 4.2 2.1 512342 87643 ? Sl 10:22 0:45 python3 app.py
www-data 8891 0.8 0.4 214532 18200 ? S 11:05 0:02 nginx: worker- VSZ: Virtual memory size in KB — includes memory-mapped files, shared libraries, and swapped-out pages. Large VSZ is not alarming by itself.
- RSS: Resident Set Size — physical RAM actually in use, in KB. This is the number to watch when diagnosing memory pressure.
- STAT codes:
R— Running or runnable (on the CPU or in the run queue)S— Interruptible sleep (waiting for an event, e.g., network data)D— Uninterruptible sleep (waiting for I/O — cannot be killed)Z— Zombie (exited, waiting for parent to reap)T— Stopped (via SIGSTOP or Ctrl+Z)s— Session leaderl— Multi-threaded+— In the foreground process group
top and htop — Real-Time Monitoring
top ships on every Linux system and updates the process list every 3 seconds by default. htop adds color, mouse support, and a cleaner layout — install it if it's not already present.
# Install htop:
sudo apt install htop # Ubuntu/Debian
sudo dnf install htop # Fedora
sudo pacman -S htop # Arch
# Launch either:
top
htopThe top header tells you a lot at a glance:
top - 14:22:01 up 5 days, 2:13, 1 user, load average: 0.52, 0.48, 0.44
Tasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie
%Cpu(s): 2.3 us, 0.8 sy, 0.0 ni, 96.5 id, 0.2 wa, 0.0 hi, 0.2 si
MiB Mem : 7934.3 total, 1203.5 free, 4821.2 used, 1909.6 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 2810.3 avail Mem- load average: Average number of processes waiting for CPU over 1, 5, and 15 minutes. On a 4-core system, sustained values above 4.0 indicate saturation.
- wa (iowait): CPU time spent waiting for I/O to complete. Consistently above 5–10% suggests a disk or NFS bottleneck.
- buff/cache: Memory the kernel has claimed for disk cache. It is released immediately when applications request RAM — do not count this as "used" when assessing memory pressure.
- avail Mem: More accurate than "free" — includes reclaimable cache.
Useful interactive keys in top: P sort by CPU, M sort by memory, k kill a PID, r renice, 1 expand individual CPU cores, u filter by user, q quit. In htop: F5 tree view, F9 signal menu, F3 search, u filter by user.
Signals and kill
Signals are asynchronous notifications sent to processes. kill is the tool to send them — the name is misleading, it sends any signal, not just termination signals.
# List all available signals:
kill -l
# SIGTERM (15) — ask the process to exit gracefully; it can catch this and clean up:
kill 1234
kill -SIGTERM 1234
# SIGKILL (9) — kernel-enforced immediate termination; process cannot intercept this:
kill -9 1234
# SIGHUP (1) — historically "terminal hangup"; daemons use it as a convention to reload config:
kill -HUP 1234
# SIGSTOP — pause a process (cannot be caught or ignored):
kill -STOP 1234
# SIGCONT — resume a stopped process:
kill -CONT 1234
# Kill all processes matching a name:
killall nginx
pkill nginx
# Send signal to processes matching a pattern:
pkill -HUP nginx # reload all nginx workers
pkill -u username # kill all processes owned by a user
# Find PIDs without killing:
pgrep nginx
pidof nginxThe correct workflow is always: try SIGTERM first, wait 5–10 seconds, then escalate to SIGKILL if the process hasn't exited. Jumping straight to kill -9 skips the process's cleanup handlers — open files may not be flushed, database transactions may be left incomplete, temp files may not be removed.
Signal Reference
| Signal | Number | Catchable | Use Case |
|---|---|---|---|
| SIGTERM | 15 | Yes | Normal shutdown — always try first |
| SIGKILL | 9 | No | Force-kill an unresponsive process |
| SIGHUP | 1 | Yes | Reload daemon config without restart |
| SIGINT | 2 | Yes | What Ctrl+C sends from the terminal |
| SIGSTOP | 19 | No | Pause a process (Ctrl+Z sends SIGTSTP, which is catchable) |
| SIGCONT | 18 | Yes | Resume a stopped process |
| SIGUSR1/2 | 10/12 | Yes | Application-defined — check the app's documentation |
Process Priority with nice and renice
The Linux scheduler assigns CPU time based on a process's nice value, ranging from -20 (highest priority — least nice to other processes) to +19 (lowest priority — most deferential). Normal processes start at 0. Only root can set negative nice values.
# Start a CPU-heavy build at low priority so it doesn't starve interactive work:
nice -n 15 make -j$(nproc)
# Start a process with elevated priority (requires root):
sudo nice -n -10 latency-sensitive-app
# Change the priority of a running process:
renice -n 10 -p 1234 # lower priority (more nice)
sudo renice -n -5 -p 1234 # higher priority (less nice, root required)
# Lower priority for all processes owned by a user:
sudo renice -n 10 -u build-user
# Verify — the NI column in ps or top shows the nice value:
ps -o pid,ni,comm -p 1234The PR (priority) column in top shows 20 + NI — so a nice value of 0 shows as PR 20, and -5 shows as PR 15. Real-time processes show rt in that column.
Practical use cases: set large compilation jobs to nice 19 on shared build servers; set backup scripts to nice 15 so they don't impact production; use ionice (see below) alongside nice to control I/O scheduling as well.
# ionice: set I/O scheduling class (useful for backup/rsync jobs)
# Class 3 = idle — only gets disk access when nothing else needs it:
ionice -c 3 rsync -av /data /backup
# Run a job with both low CPU and low I/O priority:
nice -n 19 ionice -c 3 tar czf backup.tar.gz /var/dataBackground and Foreground Jobs
# Start a command in the background immediately:
python3 myscript.py &
# Suspend a running foreground process (Ctrl+Z), then send it to background:
# [Press Ctrl+Z]
bg %1
# List current shell's background jobs:
jobs -l
# Bring a background job to the foreground:
fg %1
# Survive terminal close — nohup redirects stdout/stderr to nohup.out:
nohup python3 long_script.py &
# Detach an already-running background job from the shell:
python3 script.py &
disown %1
# For persistent background work, tmux or screen is cleaner:
tmux new -s mysession
# run your command, then Ctrl+B D to detach
tmux attach -t mysessionsystemd Service Management
On modern Linux, long-running processes belong under systemd supervision. systemd handles automatic restarts, dependency ordering, log collection via journald, and resource limits via cgroups.
# Core service operations:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx # reload config without full restart (if the unit supports it)
# Boot persistence:
sudo systemctl enable nginx # create symlink to start on boot
sudo systemctl disable nginx
sudo systemctl enable --now nginx # enable AND start in one step
# Inspect a service:
systemctl status nginx
journalctl -u nginx # all logs for this unit
journalctl -u nginx -f # follow live
journalctl -u nginx --since "1
Further Reading
