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

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.

Practicing this on a live server, or need one to follow along? See our comparison of the best Linux VPS hosting providers for 2026 if you need a Linux box to work on.

Contents
  1. Prerequisites
  2. What Is a Process
  3. ps: Snapshot of Processes
    1. Reading ps Output
  4. top and htop: Real-Time Monitoring
  5. Signals and kill
    1. Signal Reference
  6. Process Priority with nice and renice
  7. Background and Foreground Jobs
  8. systemd Service Management
    1. Further Reading

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
  • sudo access 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 (systemd on 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 state Z. 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 java

Reading 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 leader
    • l, 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
htop

The 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 nginx

The 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

SignalNumberCatchableUse Case
SIGTERM15YesNormal shutdown: always try first
SIGKILL9NoForce-kill an unresponsive process
SIGHUP1YesReload daemon config without restart
SIGINT2YesWhat Ctrl+C sends from the terminal
SIGSTOP19NoPause a process (Ctrl+Z sends SIGTSTP, which is catchable)
SIGCONT18YesResume a stopped process
SIGUSR1/210/12YesApplication-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 1234

The 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/data

Background 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 mysession

systemd 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 


JM Carreira

JM Carreira

I’ve used Linux since 2008 and it has been my main system since 2021. When an article has a “Tested on” box, I ran the steps on my Lenovo Tiny. More about me and the test machine.

Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Read our privacy policy