Skip to content
Codeloom
Linux

Linux Process Management

A comprehensive guide to Linux process management: ps, top, kill, signals, systemd services, cgroups, resource limits, and the tools you need to keep systems running.

·9 min read · By Codeloom
Intermediate 14 min read

What you'll learn

  • How to inspect running processes with ps, top, and htop
  • Signals and the right way to stop processes
  • Managing services with systemd
  • Resource limits with cgroups and ulimit
  • Debugging runaway processes and memory leaks

Prerequisites

  • Basic Linux command line usage
  • Understanding of what a process is
  • SSH access to a Linux system (for hands-on practice)

Every running program on a Linux system is a process. Understanding how to inspect, control, and manage processes is fundamental to operating any Linux server. Whether you are debugging a stuck deployment, investigating high CPU usage, or configuring a service to restart on failure, process management is the skill you reach for.

Process fundamentals

Every process has a PID (process ID), a parent PID (PPID), an owner (UID), a state, and resource usage metrics. The kernel tracks all of this.

# See your shell's PID
echo $$
# 1234

# See the parent PID
echo $PPID
# 1230

Process states

StateCodeDescription
RunningRActively using CPU or ready to run
SleepingSWaiting for an event (I/O, timer)
Uninterruptible SleepDWaiting for I/O (cannot be killed)
StoppedTPaused by a signal (SIGSTOP, Ctrl+Z)
ZombieZFinished but parent has not collected exit status

Viewing processes with ps

ps shows a snapshot of current processes.

# Your processes
ps
#   PID TTY          TIME CMD
#  1234 pts/0    00:00:00 bash
#  5678 pts/0    00:00:00 ps

# All processes, full format
ps aux
# USER    PID %CPU %MEM    VSZ   RSS TTY  STAT START   TIME COMMAND
# root      1  0.0  0.1 169456 11832 ?    Ss   Jun29   0:03 /sbin/init
# postgres 42  0.1  2.3 398756 92344 ?    Ss   Jun29   1:42 postgres

# All processes, tree format
ps auxf

# Filter by name
ps aux | grep nginx
# root     1201  0.0  0.1  65432  5432 ?  Ss  Jun29  0:00 nginx: master
# www-data 1202  0.3  0.5  67890 21234 ?  S   Jun29  2:15 nginx: worker

# Show specific columns
ps -eo pid,ppid,user,%cpu,%mem,stat,start,command --sort=-%cpu | head -20

Finding processes

# Find by name
pgrep -la nginx
# 1201 nginx: master process
# 1202 nginx: worker process

# Find by port
ss -tlnp | grep :8080
# Or with lsof
lsof -i :8080

# Find by user
ps -u postgres

# Process tree for a specific PID
pstree -p 1201

Interactive monitoring with top and htop

top provides a live view of system resource usage.

top

Key information in top:

top - 14:32:01 up 45 days, load average: 1.23, 0.98, 0.87
Tasks: 234 total,   2 running, 232 sleeping,   0 stopped,   0 zombie
%Cpu(s):  5.2 us,  1.3 sy,  0.0 ni, 93.2 id,  0.3 wa,  0.0 hi,  0.0 si
MiB Mem:  16384.0 total,   2048.0 free,  10240.0 used,   4096.0 buff/cache
MiB Swap:  4096.0 total,   4000.0 free,     96.0 used.   5632.0 avail Mem

Key fields:

  • load average: 1-min, 5-min, 15-min average number of processes in the run queue. Compare to CPU count.
  • us: user-space CPU (your apps)
  • sy: kernel CPU (system calls)
  • wa: I/O wait (process waiting for disk)
  • id: idle

Useful top shortcuts:

  • P - sort by CPU
  • M - sort by memory
  • k - kill a process (enter PID)
  • 1 - show per-CPU stats
  • c - show full command line
  • q - quit

htop is a better version of top with colors, mouse support, and tree view.

# Install if not present
sudo apt install htop  # Debian/Ubuntu
sudo yum install htop  # RHEL/CentOS

htop

Signals

Signals are the mechanism for communicating with processes. kill sends signals (despite the name, it does not always kill).

# Common signals
kill -l  # List all signals

# SIGTERM (15) - polite shutdown request (default)
kill 1234
kill -15 1234
kill -SIGTERM 1234

# SIGKILL (9) - force kill (cannot be caught)
kill -9 1234
kill -SIGKILL 1234

# SIGHUP (1) - reload configuration
kill -1 1234
kill -SIGHUP 1234

# SIGSTOP (19) - pause process
kill -STOP 1234

# SIGCONT (18) - resume paused process
kill -CONT 1234

# SIGUSR1/SIGUSR2 - user-defined (apps use for custom actions)
kill -USR1 1234

Signal handling best practices

Always try SIGTERM first. Give the process time to clean up. Only use SIGKILL as a last resort.

# Graceful shutdown pattern
kill $PID
sleep 5

# Check if still running
if kill -0 $PID 2>/dev/null; then
    echo "Process did not stop gracefully, force killing"
    kill -9 $PID
fi

Kill by name

# Kill all processes matching a name
pkill nginx

# Kill with signal
pkill -HUP nginx

# Kill all processes by a user
pkill -u baduser

# Kill processes matching a pattern
pkill -f "python manage.py runserver"

Foreground and background jobs

# Run in background
./long-task.sh &
# [1] 12345

# List background jobs
jobs
# [1]+  Running  ./long-task.sh &

# Bring to foreground
fg %1

# Suspend current foreground process
# Press Ctrl+Z
# [1]+  Stopped  ./long-task.sh

# Resume in background
bg %1

# Disown a job (keeps running after logout)
disown %1

nohup for persistent processes

# Run immune to hangups
nohup ./server.sh &
# Output goes to nohup.out by default

# With custom log file
nohup ./server.sh > /var/log/server.log 2>&1 &
echo "Server PID: $!"

systemd service management

systemd is the init system on modern Linux distributions. It manages services (daemons) as units.

# Start/stop/restart
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

# Reload config without full restart
sudo systemctl reload nginx

# Enable at boot / disable
sudo systemctl enable nginx
sudo systemctl disable nginx

# Check status
systemctl status nginx
# nginx.service - A high performance web server
#    Loaded: loaded (/etc/systemd/system/nginx.service; enabled)
#    Active: active (running) since Mon 2026-06-29 10:00:00 UTC; 1 day ago
#  Main PID: 1201 (nginx)
#     Tasks: 5 (limit: 4915)
#    Memory: 12.3M
#       CPU: 1min 42s

# View logs
journalctl -u nginx
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx -f  # Follow (tail)

Writing a systemd service file

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8000
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/myapp/data /var/log/myapp

# Resource limits
MemoryMax=512M
CPUQuota=200%

# Environment
Environment=NODE_ENV=production
EnvironmentFile=/opt/myapp/.env

[Install]
WantedBy=multi-user.target
# After creating or modifying a service file
sudo systemctl daemon-reload
sudo systemctl start myapp
sudo systemctl enable myapp

Restart policies

Restart= valueBehavior
noNever restart
on-failureRestart on non-zero exit
on-abnormalRestart on signal, timeout, or watchdog
on-abortRestart on signal only
alwaysAlways restart (even on clean exit)

Resource limits with ulimit

ulimit sets per-user resource limits for the current shell session.

# View all limits
ulimit -a

# Max open files (commonly needs increase for web servers)
ulimit -n
# 1024 (default)

ulimit -n 65536
# Set to 65536 for this session

# Max processes
ulimit -u

# Max file size
ulimit -f

# Core dump size (0 = disabled)
ulimit -c

For permanent limits, edit /etc/security/limits.conf:

# /etc/security/limits.conf
appuser  soft  nofile  65536
appuser  hard  nofile  65536
appuser  soft  nproc   4096
appuser  hard  nproc   4096

cgroups: resource control

cgroups (control groups) limit and isolate resource usage for groups of processes. systemd uses cgroups under the hood.

# View cgroup hierarchy
systemd-cgls

# Resource usage by cgroup
systemd-cgtop

# View a service's cgroup limits
systemctl show myapp.service | grep -i memory
systemctl show myapp.service | grep -i cpu

Setting cgroup limits via systemd

# In your service file's [Service] section

# Memory limits
MemoryMax=1G          # Hard limit
MemoryHigh=768M       # Throttle point

# CPU limits
CPUQuota=150%         # 1.5 CPUs worth
CPUWeight=100         # Relative weight (default 100)

# I/O limits
IOWeight=100          # Relative I/O weight
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 50M

# Process limits
TasksMax=512          # Max number of tasks/threads
# Apply changes
sudo systemctl daemon-reload
sudo systemctl restart myapp

# Verify
systemctl show myapp.service -p MemoryMax

Debugging runaway processes

High CPU

# Find the culprit
top -bn1 | head -20

# Get thread-level detail
top -H -p $PID

# See what the process is doing (system calls)
strace -p $PID -c
# Press Ctrl+C after a few seconds to see summary

# CPU profiling (if perf is installed)
sudo perf top -p $PID

High memory

# Sort by memory usage
ps aux --sort=-%mem | head -10

# Detailed memory map of a process
pmap -x $PID

# Check for memory leaks over time
while true; do
    ps -p $PID -o rss= >> /tmp/mem_$PID.log
    sleep 60
done

Too many open files

# Count open files for a process
ls /proc/$PID/fd | wc -l

# See what files are open
lsof -p $PID | head -30

# System-wide open file count
cat /proc/sys/fs/file-nr
# 3456  0  65536 (allocated, free, max)

Zombie processes

# Find zombies
ps aux | awk '$8 ~ /Z/ {print}'

# Find the parent of a zombie
ps -o ppid= -p $ZOMBIE_PID

# The parent needs to call wait(). Sending SIGCHLD to the parent sometimes helps
kill -SIGCHLD $PARENT_PID

# If the parent is stuck, killing the parent clears the zombies
# (init/systemd adopts and reaps them)

Useful process management commands

# Run with lower priority
nice -n 10 ./cpu-intensive-task.sh

# Change priority of running process
renice -n 5 -p $PID

# Run in background, get PID
./server.sh &
echo "Server PID: $!"

# Wait for background process
./task.sh &
task_pid=$!
wait $task_pid
echo "Task exited with code: $?"

# Timeout a command
timeout 30s curl http://slow-api.example.com

# Watch a command repeatedly
watch -n 2 'ps aux --sort=-%cpu | head -10'

# Track process creation in real time
sudo execsnoop-bpfcc  # requires bcc-tools

Process management is the foundation of Linux system administration. Know how to find what is running, understand what it is doing, control its resources, and stop it cleanly. These tools and patterns apply whether you are managing a single server or a fleet of thousands.