Skip to main content
Linux·9 min read

Linux Process at 100% CPU — How to Find It, Kill It, and Prevent It

Master Linux process management for production troubleshooting. Covers ps output interpretation, top/htop for real-time monitoring, signal handling, zombie processes, and background job control.

DT

DevOps Engineer & Technical Writer

The Problem

An application is consuming 100% CPU. A zombie process is holding a port. You need to find which process owns a file, send it a graceful shutdown signal, and verify it terminated. Process management is the foundation of Linux troubleshooting.

Process Lifecycle — States and Transitions fork() create child exec() load program Running (R) on CPU / runnable top / ps shows R Sleep (S) waiting Stop (T) SIGSTOP Zombie (Z) defunct exit() terminate Parent wait() reaps child If parent never calls wait(), child becomes Zombie (Z state)

ps — View Running Processes

ps aux                                    # All processes, full detail

ps auxf # Tree format (parent-child)

ps -u deploy # Specific user

pgrep -a nginx # Find by name

ps -eo pid,ppid,user,%cpu,%mem,stat,start,time,command --sort=-%mem | head -20

ps -eo pid,nlwp,command --sort=-nlwp | head -20 # Thread count

Reading ps output

USER       PID %CPU %MEM    VSZ   RSS TTY  STAT START   TIME COMMAND

www-data 1234 45.2 8.3 2345678 678900 ? Sl 10:23 12:34 java -jar app.jar

deploy 5678 0.0 0.0 0 0 ? Z Jan12 0:00 [defunct]

  • RSS — Actual physical memory used (use this for capacity planning)
  • VSZ — Virtual memory (includes unmapped space)
  • STAT — Process state

Process states

StateMeaning
<code class="inline-code">R</code>Running or runnable
<code class="inline-code">S</code>Sleeping (waiting)
<code class="inline-code">D</code>Uninterruptible sleep (I/O)
<code class="inline-code">Z</code>Zombie (terminated, not reaped)
<code class="inline-code">T</code>Stopped
<code class="inline-code">s</code>Session leader
<code class="inline-code">l</code>Multi-threaded

top and htop

top                    # Real-time view

top -o %MEM # Sort by memory

top -u deploy # Specific user

top -bn1 | head -20 # Batch mode for scripts

htop --tree # Interactive with tree view

top keyboard shortcuts

KeyAction
<code class="inline-code">M</code>Sort by memory
<code class="inline-code">P</code>Sort by CPU
<code class="inline-code">k</code>Kill a process
<code class="inline-code">c</code>Toggle full command
<code class="inline-code">H</code>Show threads
<code class="inline-code">1</code>Per-CPU usage
<code class="inline-code">q</code>Quit

Finding Processes

By name

pgrep -a nginx

pgrep -l nginx

pgrep -c nginx

pidof nginx

By port

ss -tlnp | grep 8080

lsof -i :8080

fuser 8080/tcp

By file

lsof /var/log/app/application.log

lsof +D /var/log/app/

lsof +L1 | grep deleted # Invisible disk space usage

By resource usage

ps aux --sort=-%cpu | head -11

ps aux --sort=-%mem | head -11

ps aux | awk '$6 > 1048576 {print $0}' # RSS > 1GB

Sending Signals

Common signals

SignalNumberBehavior
<code class="inline-code">SIGHUP</code>1Reload config
<code class="inline-code">SIGINT</code>2Interrupt (Ctrl+C)
<code class="inline-code">SIGTERM</code>15Graceful termination
<code class="inline-code">SIGKILL</code>9Force kill (uncatchable)
<code class="inline-code">SIGUSR1</code>10User-defined
<code class="inline-code">SIGSTOP</code>19Pause process
<code class="inline-code">SIGCONT</code>18Resume process

Usage

kill 1234                  # SIGTERM (graceful)

kill -9 1234 # SIGKILL (force)

kill -HUP 1234 # Reload config

pkill nginx # Kill by name

pkill -9 nginx # Force kill by name

pkill -u baduser # Kill all user processes

kill -TERM -1234 # Kill process group

Graceful shutdown pattern

kill $PID

sleep 5

if kill -0 $PID 2>/dev/null; then

echo "Still running, force killing"

kill -9 $PID

fi

Background Jobs

# Run in background

long-running-command &

# Disown (survives shell exit)

long-running-command &

disown

# Immune to hangups

nohup long-running-command > output.log 2>&1 &

# tmux (best for interactive)

tmux new -s deploy

# ... run commands ...

# Ctrl+B, D to detach

tmux attach -t deploy

Job control

jobs          # List background jobs

fg %1 # Bring to foreground

# Ctrl+Z to pause, then:

bg %1 # Send to background

wait $PID # Wait for completion

Zombie Processes

# Find zombies

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

# Find parent of zombies

ps -eo pid,ppid,stat,command | grep "Z"

# Fix: kill or restart the parent process

# Zombies themselves cannot be killed

Process Priority

nice -n 10 heavy-task.sh         # Start with lower priority

nice -n -20 critical-task.sh # Highest priority (root)

renice 10 -p 1234 # Change running process

renice -5 -p 1234 # Higher priority (root)

Nice values: -20 (highest priority) to 19 (lowest). Default is 0.

Troubleshooting Patterns

CPU hog

top -bn1 -o %CPU | head -15

ps -T -p 1234 -o spid,pcpu,time,command | sort -k2 -rn | head -20

pidstat -p 1234 2

Memory hog

ps aux --sort=-rss | head -20

pmap -x 1234

Disk I/O

iotop -o

pidstat -d -p 1234 2

Common Mistakes

  • Using kill -9 first — SIGKILL skips cleanup (temp files, locks, connections). Always try SIGTERM first.
  • Grep matching itselfps aux | grep nginx matches grep. Use pgrep -a nginx instead.
  • Ignoring zombies — A few are harmless, but hundreds indicate a parent bug. Fix the parent.
  • Background without nohup or tmux — Closing SSH kills child processes. Use nohup, tmux, or systemd.
  • Confusing RSS and VSZ — VSZ includes unmapped memory. RSS is actual physical usage.
  • Killing by name without verificationpkill java kills ALL Java processes. Use pkill -f "java.*specific-app".
  • Quick Reference

    TaskCommand
    All processes<code class="inline-code">ps aux</code>
    Process tree<code class="inline-code">ps auxf</code>
    Find by name<code class="inline-code">pgrep -a name</code>
    Find by port<code class="inline-code">ss -tlnp \grep PORT</code>
    Top CPU users<code class="inline-code">ps aux --sort=-%cpu \head</code>
    Top memory users<code class="inline-code">ps aux --sort=-%mem \head</code>
    Graceful kill<code class="inline-code">kill PID</code>
    Force kill<code class="inline-code">kill -9 PID</code>
    Reload config<code class="inline-code">kill -HUP PID</code>
    Background job<code class="inline-code">command &amp;</code>
    Survive SSH<code class="inline-code">nohup command &amp;</code> or <code class="inline-code">tmux</code>
    Find zombies<code class="inline-code">ps aux \awk &#39;$8~/Z/&#39;</code>
    Process I/O<code class="inline-code">iotop -o</code>
    Change priority<code class="inline-code">renice 10 -p PID</code>

    Summary

    Process management starts with identification (ps, pgrep, lsof), monitoring (top, htop), and control (kill, nice, jobs). Always SIGTERM before SIGKILL. Use tmux or systemd for persistent processes. Start troubleshooting with ps aux --sort=-%cpu or --sort=-%mem to find the resource hog immediately.

    ---

    Frequently Asked Questions

    How do I find and kill a process in Linux?

    Use ps aux | grep <process-name> or pgrep <name> to find the process ID. Then use kill <pid> for graceful termination (SIGTERM) or kill -9 <pid> for forced termination (SIGKILL). For killing by name, use pkill <name> or killall <name>. Always try SIGTERM first to allow the process to clean up before using SIGKILL.

    What is the difference between SIGTERM and SIGKILL?

    SIGTERM (signal 15) asks the process to terminate gracefully — it can be caught, handled, and ignored by the process, allowing cleanup of resources. SIGKILL (signal 9) immediately terminates the process at the kernel level — it cannot be caught or ignored. Always use SIGTERM first and only escalate to SIGKILL if the process doesn't respond within a reasonable timeout.

    How do I run a process in the background on Linux?

    Append & to run a command in the background: ./script.sh &. Use nohup ./script.sh & to keep it running after logout. For better management, use screen or tmux sessions, or create a systemd service for production processes. Use jobs to list background processes and fg %1 to bring one to the foreground.

    How do I check what resources a process is using?

    Use top -p <pid> for real-time CPU and memory monitoring of a specific process. For more detail, check /proc/<pid>/status for memory breakdown, strace -p <pid> for system call activity, and lsof -p <pid> for open files and network connections. Use pidstat -p <pid> 1 for periodic per-process CPU, memory, and I/O statistics.

    What is a zombie process and how do I fix it?

    A zombie process has finished execution but its parent hasn't collected its exit status with wait(). It appears as <defunct> in ps output and consumes a process table entry but no resources. Fix by sending SIGCHLD to the parent process (kill -SIGCHLD <parent-pid>) or killing the parent, which makes init adopt and reap the zombie.

    ---