Tutorial

Process Management on Linux: ps, top, htop, kill, and nice

April 28, 2026

Back to Blog

Why Process Management Matters

Every command you run, every service you start, and every script you execute on a Linux system creates a process. Understanding how to monitor, control, and manage these processes is one of the most fundamental skills a Linux administrator can possess. Whether you are tracking down a runaway application consuming all your CPU, gracefully restarting a web server, or tuning the priority of a backup job so it does not interfere with production traffic, process management is the toolbox you reach for daily.

In this comprehensive tutorial, we will walk through every essential process management tool in the Linux ecosystem. You will learn to inspect running processes with ps and top, gain visual superpowers with htop, send precise signals with kill, and fine-tune CPU scheduling with nice and renice. Along the way, we will explore the /proc filesystem, understand zombie processes, and master background job control.

Who is this for? System administrators, DevOps engineers, and developers who manage Linux servers. You should be comfortable with the terminal, but no advanced kernel knowledge is required.

Understanding Linux Processes

A process is simply a running instance of a program. When you type ls in your terminal, the kernel creates a new process, assigns it a unique Process ID (PID), allocates memory, and begins execution. Once ls finishes listing files, the process terminates and the PID is freed.

Every process has a parent process. The very first process started by the kernel is init (or systemd on modern distributions), which receives PID 1. All other processes descend from it, forming a tree structure.

Key Process Attributes

AttributeDescriptionExample
PIDUnique process identifier1842
PPIDParent process ID1
UIDUser who owns the process1000 (ubuntu)
StateCurrent status (Running, Sleeping, Stopped, Zombie)S (sleeping)
NiceCPU scheduling priority (-20 to 19)0 (default)
TTYAssociated terminalpts/0
RSSResident Set Size (physical memory used)12480 KB
VSZVirtual memory size225600 KB

Inspecting Processes with ps

The ps command is your first stop for examining processes. It provides a snapshot of running processes at the moment you execute it (unlike top, which updates continuously).

The Most Common Invocations

$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 169584 13256 ? Ss Mar10 0:12 /sbin/init root 482 0.0 0.0 16124 7424 ? Ss Mar10 0:01 /lib/systemd/systemd-journald www-data 3847 0.2 1.4 512340 45280 ? S 09:14 0:05 nginx: worker process mysql 4012 1.3 8.2 1842560 262144 ? Ssl Mar10 48:22 /usr/sbin/mysqld ubuntu 5291 0.0 0.0 10072 3648 pts/0 R+ 10:22 0:00 ps aux

The aux flags tell ps to show all users, display user-oriented format, and include processes not attached to a terminal (x). This is by far the most common usage.

$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 Mar10 ? 00:00:12 /sbin/init root 482 1 0 Mar10 ? 00:00:01 /lib/systemd/systemd-journald www-data 3847 3844 0 09:14 ? 00:00:05 nginx: worker process

The -ef variant uses POSIX-standard syntax and importantly shows the PPID (parent PID), which is invaluable when tracing process relationships.

Filtering and Formatting

Rather than piping through grep every time, ps supports powerful filtering options:

# Show only processes for user "www-data" $ ps -u www-data -o pid,ppid,%cpu,%mem,comm PID PPID %CPU %MEM COMMAND 3847 3844 0.2 1.4 nginx 3848 3844 0.1 1.2 nginx 4501 4499 0.0 0.8 php-fpm # View process tree $ ps -ejH PID PGID SID TTY TIME CMD 1 1 1 ? 00:00:12 systemd 482 482 482 ? 00:00:01 systemd-journald 3844 3844 3844 ? 00:00:00 nginx 3847 3844 3844 ? 00:00:05 nginx # Top 5 memory-consuming processes $ ps aux --sort=-%mem | head -6

Real-Time Monitoring with top

While ps gives you a snapshot, top provides a live, continuously updating view of your system. It refreshes every 3 seconds by default and displays CPU usage, memory consumption, load average, and a sortable process list.

$ top top - 10:34:22 up 7 days, 1:14, 2 users, load average: 0.42, 0.38, 0.35 Tasks: 187 total, 1 running, 186 sleeping, 0 stopped, 0 zombie %Cpu(s): 3.2 us, 1.1 sy, 0.0 ni, 95.4 id, 0.2 wa, 0.0 hi, 0.1 si MiB Mem : 7953.2 total, 2104.8 free, 3248.4 used, 2600.0 buff/cache MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 4412.8 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4012 mysql 20 0 1842560 262144 25600 S 2.3 3.2 48:22.4 mysqld 3847 www-data 20 0 512340 45280 12800 S 0.7 0.6 0:05.2 nginx

Essential top Interactive Commands

KeyAction
PSort by CPU usage (default)
MSort by memory usage
TSort by cumulative time
kKill a process (prompts for PID)
rRenice a process (change priority)
uFilter by user
1Toggle per-CPU core display
cToggle full command line vs command name
dChange refresh interval (default 3s)
qQuit top
Batch Mode Tip: Use top -bn1 to run top once in batch mode, perfect for scripting and log collection. For example: top -bn1 | head -20 > /var/log/top_snapshot.txt

Visual Process Management with htop

htop is the modern, interactive, and visually rich alternative to top. It provides color-coded CPU/memory bars, mouse support, tree views, and the ability to search, filter, and kill processes without memorizing PID numbers.

Installing htop

# Ubuntu/Debian $ sudo apt install htop # RHEL/CentOS/Fedora $ sudo dnf install htop # Arch Linux $ sudo pacman -S htop

htop Interface Breakdown

When you launch htop, the screen is divided into three sections:

1
Header: CPU bars (one per core), memory and swap bars, task counts, load average, and uptime. Each CPU core gets its own visual bar showing user (green), system (red), and nice (blue) time.
2
Process List: Sortable, scrollable list of all processes with PID, user, priority, nice value, virtual/resident memory, state, CPU%, memory%, time, and command. Use arrow keys to scroll and F6 to choose a sort column.
3
Function Bar: Bottom row shows F1-F10 shortcuts. F3 searches, F4 filters, F5 toggles tree view, F9 kills, F2 opens setup.
Tree View (F5): This is one of htop's most powerful features. It shows processes in a hierarchical tree, making it trivial to see which child processes belong to which parent. Perfect for understanding how nginx spawns workers or how a shell script forks subprocesses.

htop vs top

Featuretophtop
Color outputNoYes
Mouse supportNoYes
Process treeNoYes (F5)
Scroll horizontallyNoYes
Search/filterBasicAdvanced
Kill without PIDNo (need PID)Yes (select + F9)
Installed by defaultYesNo (apt install)
Per-core CPU barsToggle (1)Always visible

Sending Signals with kill

Despite its name, kill does not always kill processes. It sends signals — messages from the kernel to a process instructing it to take a specific action. Termination is just one of many possible signals.

The Most Important Signals

SignalNumberDefault ActionCan Be Caught?Use Case
SIGTERM15Terminate gracefullyYesAsk process to clean up and exit
SIGKILL9Force kill immediatelyNoLast resort when process is stuck
SIGHUP1Hangup / reload configYesReload nginx, Apache, sshd configs
SIGSTOP19Pause processNoFreeze a process without killing it
SIGCONT18Resume paused processYesResume a stopped process
SIGINT2Interrupt (Ctrl+C)YesUser interrupt from terminal
SIGUSR110User-definedYesApplication-specific (log rotation, etc.)

Using kill, killall, and pkill

# Send SIGTERM (graceful) to PID 4012 $ kill 4012 # Send SIGKILL (force) to PID 4012 $ kill -9 4012 # Reload nginx configuration (SIGHUP) $ kill -HUP $(cat /var/run/nginx.pid) # Kill all processes named "php-fpm" $ killall php-fpm # Kill processes matching a pattern $ pkill -f "python3 backup_script.py" # Kill all processes owned by user "baduser" $ pkill -u baduser
Never jump straight to kill -9! Always try kill (SIGTERM) first. SIGTERM allows the process to close files, flush buffers, release locks, and clean up temporary files. SIGKILL terminates immediately with no cleanup, which can lead to data corruption, stale PID files, and orphaned child processes.

Finding Processes with pgrep

pgrep is the companion to pkill. Instead of killing processes, it lists matching PIDs:

# Find all nginx PIDs $ pgrep nginx 3844 3847 3848 # Find with process names $ pgrep -la nginx 3844 nginx: master process /usr/sbin/nginx 3847 nginx: worker process 3848 nginx: worker process # Count matching processes $ pgrep -c php-fpm 12

CPU Priority with nice and renice

Linux uses a priority system to decide how much CPU time each process gets. The nice value ranges from -20 (highest priority) to 19 (lowest priority). The default is 0. Regular users can only increase the nice value (lower priority), while root can set any value.

How Nice Values Affect Scheduling

Nice -20
Highest Priority
Nice 0
Default
Nice 19
Lowest Priority
# Start a backup with low priority (won't steal CPU from web server) $ nice -n 15 tar czf /backup/site.tar.gz /var/www/ # Start a critical process with high priority (root only) $ sudo nice -n -10 /opt/critical-service # Change priority of a running process $ sudo renice -n 10 -p 4012 4012 (process ID) old priority 0, new priority 10 # Renice all processes for a user $ sudo renice -n 15 -u baduser
Practical Tip: Always run backup jobs, log compression, and batch processing with nice -n 10 or higher. This ensures these heavy tasks yield CPU time to interactive services like your web server and database.

The /proc Filesystem

The /proc filesystem is a virtual filesystem that provides a window into the kernel's view of every running process. Each process has a directory at /proc/[PID]/ containing detailed information.

Exploring /proc

# Full command line of PID 4012 $ cat /proc/4012/cmdline | tr '\0' ' ' /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql # Memory map of a process $ cat /proc/4012/status | grep -i mem VmPeak: 1842560 kB VmSize: 1842560 kB VmRSS: 262144 kB # Current working directory $ ls -l /proc/4012/cwd lrwxrwxrwx 1 mysql mysql 0 Mar 10 09:14 /proc/4012/cwd -> /var/lib/mysql # Open file descriptors $ ls /proc/4012/fd | wc -l 47 # Environment variables $ cat /proc/4012/environ | tr '\0' '\n'

Useful /proc Files

PathContent
/proc/[PID]/statusHuman-readable process status (name, state, memory, threads)
/proc/[PID]/cmdlineComplete command line arguments
/proc/[PID]/fd/Open file descriptors (symlinks to actual files)
/proc/[PID]/mapsMemory map (shared libraries, heap, stack)
/proc/[PID]/ioI/O statistics (bytes read/written)
/proc/[PID]/limitsResource limits (open files, memory, etc.)
/proc/loadavgSystem load average (1, 5, 15 minutes)
/proc/meminfoSystem memory breakdown
/proc/cpuinfoCPU model, cores, frequency, flags

Zombie Processes: The Walking Dead of Linux

A zombie process (state Z) is a process that has finished executing but still occupies an entry in the kernel's process table. This happens when a child process exits but its parent has not yet called wait() to collect its exit status.

Are Zombies Dangerous?

Individual zombies consume no CPU or memory — they are just a row in the process table. However, thousands of zombies can exhaust the PID space (default maximum is 32768), preventing new processes from being created.

How to Fix Zombies

You cannot kill a zombie with kill -9 — it is already dead. The solution is to send SIGCHLD to the parent process (kill -SIGCHLD [PPID]) or kill the parent entirely, allowing init to reap the zombies.

# Find zombie processes $ ps aux | awk '$8 ~ /Z/ {print}' nobody 12345 0.0 0.0 0 0 ? Z 10:00 0:00 [defunct] # Find the parent of the zombie $ ps -o ppid= -p 12345 6789 # Ask parent to reap its children $ kill -SIGCHLD 6789 # If that doesn't work, kill the parent $ kill 6789

Background and Foreground Job Control

Linux provides powerful job control mechanisms that let you run processes in the background, bring them to the foreground, suspend and resume them, and keep them running after you disconnect from your SSH session.

The Basics: &, Ctrl+Z, bg, fg, jobs

# Run a command in the background with & $ tar czf /backup/huge-backup.tar.gz /var/www/ & [1] 15432 # List background jobs $ jobs [1]+ Running tar czf /backup/huge-backup.tar.gz /var/www/ & # Bring job 1 back to foreground $ fg %1 # Suspend the foreground process (Ctrl+Z) ^Z [1]+ Stopped tar czf /backup/huge-backup.tar.gz /var/www/ # Resume it in the background $ bg %1 [1]+ tar czf /backup/huge-backup.tar.gz /var/www/ &

Surviving SSH Disconnection: nohup and disown

When you close an SSH session, the shell sends SIGHUP to all child processes, which typically kills them. Two tools prevent this:

nohup (before starting)

$ nohup ./long-process.sh & nohup: ignoring input and appending output to 'nohup.out'

Use nohup before starting the command. Output is redirected to nohup.out by default.

disown (after starting)

$ ./long-process.sh & [1] 18234 $ disown %1

Use disown after starting the command if you forgot to use nohup.

Pro Tip: For long-running tasks on remote servers, consider using tmux or screen instead. These terminal multiplexers let you detach and reattach sessions, which is far more flexible than nohup.

Practical Scenarios

Scenario 1: Finding a Memory Leak

# Sort by memory usage, show top 10 $ ps aux --sort=-%mem | head -11 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND mysql 4012 1.3 32.8 2684560 1048576 ? Ssl Mar10 48:22 mysqld www-data 8891 0.0 12.4 512340 398336 ? S 09:00 0:05 php-fpm: pool www # Watch a specific process over time $ watch -n 5 'ps -o pid,rss,vsz,comm -p 8891' PID RSS VSZ COMMAND 8891 398336 512340 php-fpm # If RSS keeps growing, restart the service gracefully $ kill -USR2 $(cat /var/run/php-fpm.pid)

Scenario 2: Stopping a Runaway Cron Job

# Find the runaway script $ pgrep -af "backup_all.sh" 22145 /bin/bash /opt/scripts/backup_all.sh 22148 tar czf /backup/full.tar.gz /var/www/ # Kill the parent script (children die automatically) $ kill 22145 # Verify they're gone $ pgrep -af "backup_all.sh" (no output - processes are terminated)

Scenario 3: Checking Open Files and Connections

# How many files does nginx have open? $ ls /proc/$(pgrep -o nginx)/fd | wc -l 128 # What's the file descriptor limit? $ cat /proc/$(pgrep -o nginx)/limits | grep "Max open files" Max open files 65536 65536 files # Find processes using a specific port $ ss -tlnp | grep :443 LISTEN 0 511 *:443 *:* users:(("nginx",pid=3844,fd=8))

Process Management at Scale with Panelica

On a shared hosting server with dozens or hundreds of users, manual process management becomes impractical. You need automated controls that prevent any single user from monopolizing server resources.

Panelica handles this through Cgroups v2 integration. Each user account is placed inside a dedicated cgroup slice at /sys/fs/cgroup/panelica.slice/panelica-user.slice/panelica-user-{username}.slice/. Within that slice, the panel configures:

  • pids.max — Maximum number of processes a user can spawn, preventing fork bombs
  • cpu.max — CPU time quota, ensuring fair distribution across all users
  • memory.max — Hard memory ceiling to prevent any user from triggering OOM kills
  • io.max — Disk I/O throughput limits to stop heavy backup tasks from saturating storage

This means if a user's PHP script enters an infinite loop or their cron job forks thousands of child processes, the cgroup's pids.max instantly blocks new process creation. The rest of the server is completely unaffected. Panelica also runs dedicated cgroup watcher daemons for SSH sessions, PHP-FPM pools, and FTP connections, monitoring resource usage per user in real time and enforcing limits before problems escalate.

The key advantage: Instead of manually running ps, kill, and renice every time a user misbehaves, Panelica's cgroup enforcement is automatic, immediate, and per-user. Administrators can set limits through the panel's UI and forget about them — the kernel handles enforcement.

Quick Reference Cheat Sheet

TaskCommand
List all processesps aux
Show process treeps -ejH or pstree
Find process by namepgrep -la nginx
Monitor in real-timehtop or top
Graceful terminatekill PID
Force terminatekill -9 PID
Reload configkill -HUP PID
Kill by namekillall name or pkill name
Run low prioritynice -n 15 command
Change running priorityrenice -n 10 -p PID
Run in backgroundcommand &
Survive SSH disconnectnohup command &
Detach running jobdisown %1
Find zombiesps aux | awk '$8=="Z"'

Conclusion

Process management is a daily activity for anyone who works with Linux servers. The tools we have covered — ps for snapshots, top and htop for real-time monitoring, kill and its variants for sending signals, and nice/renice for priority tuning — form the essential toolkit that every administrator needs. Combined with an understanding of the /proc filesystem, zombie processes, and job control, you can diagnose and resolve virtually any process-related issue.

Start by making htop your default go-to instead of top. Practice using pgrep and pkill instead of the fragile ps aux | grep pipeline. And always remember: SIGTERM before SIGKILL, nice your heavy tasks, and keep an eye on those zombies.

Share:
See the Demo