Linux Process Management and Troubleshooting

Tested on RHEL 10.2 (Coughlan)
Package procps-ng 4.0.4-11.el10.x86_64
psmisc 23.6-8.el10.x86_64
Applies to Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux
Privilege Normal user for process listings and monitoring; sudo or root to signal processes owned by other users or access restricted process details
Scope Integrated workflow to find, inspect, monitor, signal, and troubleshoot processes with ps, pgrep, top, and kill. Covers shell jobs versus system processes, parent-child trees, and zombie and D states at a practical level. Does not replace full ps, top, or kill command references.
Related guides List processes in Linux
ps command
top command
kill and pkill
nice and renice

Every running program on Linux becomes a process with a process ID (PID), an owner, a state, and a parent. Administrators spend most troubleshooting time tracing one misbehaving PID through listing tools, live monitors, signals, and service ownership — not memorizing every ps flag in isolation.

This guide walks that end-to-end workflow on a single host. For flag-level depth on individual tools, use the linked command references in each section.


What is a Linux process?

A program is a file on disk (/usr/bin/bash, a compiled binary, or a script). A process is that program while it is executing. The kernel tracks each process with:

  • PID — unique numeric ID for this running instance
  • PPID — parent PID; most user commands are children of your shell or of systemd
  • User — UID that owns the process; determines what files and signals you can use against it
  • Command — argv shown by ps (may truncate long lines)
  • State — running, sleeping, stopped, zombie, and other codes in the STAT column
  • Threads — one process can have multiple kernel-scheduled threads; ps and top usually show the main thread or task count depending on options

Kernel threads and user daemons appear in the same tables as your shell commands. systemd (PID 1 on modern distros) is the root of most service trees.


List, find, and trace parent-child processes

Start with a snapshot of what is running. The ps command reads process data from /proc; see also list processes in Linux for user and name filters.

ps aux and ps -ef request a system-wide listing. Plain ps without those flags normally shows only processes tied to your user and terminal.

List every process with owner, CPU, memory, and state (BSD style):

bash
ps aux
output
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.2  44580 17752 ?        Ss   Aug04   0:16 /usr/lib/systemd/systemd --switched-root --system --deserialize=43 rhgb

SysV-style full format includes parent PID and start time:

bash
ps -ef
output
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 Aug04 ?        00:00:16 /usr/lib/systemd/systemd --switched-root --system --deserialize=43 rhgb

pidof returns PIDs for a binary name when the executable path matches:

bash
pidof bash
output
41078 33715 32125

Filter by user:

bash
ps -u "$(whoami)" -o pid,stat,cmd

Replace $(whoami) with a username to inspect another account when you have permission.

PPID links every process to its parent. A runaway worker may be harmless to kill; a parent systemd unit or wrapper script may respawn it seconds later.

Forest view for one process tree:

bash
ps -ef --forest | head -20

pstree shows the same hierarchy with PIDs attached:

bash
pstree -p | head -15
output
systemd(1)-+-ModemManager(1156)-+-{ModemManager}(1162)
           |-NetworkManager(1080)-+-{NetworkManager}(1105)
           |-accounts-daemon(1088)-+-{accounts-daemon}(1129)

When a process is managed by systemd, stop the unit instead of only killing the worker PID:

bash
systemctl status sshd
output
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-08-04 10:08:28 IST; 1 day 8h ago

Use systemctl stop or systemctl restart on the unit name when the exam or runbook expects service-level control.

RHEL-family systems use sshd.service. Debian and Ubuntu commonly use ssh.service, so confirm the unit name before running systemctl stop or systemctl restart.


Process states, zombies, and uninterruptible sleep

The STAT column in ps encodes state plus optional modifiers. Common base states:

Code Meaning
R Running or runnable — on the CPU or waiting for a CPU slot
S Interruptible sleep — waiting for an event (timer, network, disk)
D Uninterruptible sleep — blocked on I/O; SIGKILL may not end the task until the kernel I/O completes or fails
T Stopped — job control or debugger
Z Zombie — exited but parent has not collected exit status

D means uninterruptible sleep, while SIGKILL does not give the process an opportunity to clean up. Modifiers such as s (session leader), l (multi-threaded), and + (foreground job) appear after the letter. A sleeping S process is normal for idle daemons; state alone does not prove a bug.

Inspect state for the current shell (PID $$ is always available):

bash
ps -p $$ -o pid,ppid,user,stat,cmd
output
PID    PPID USER     STAT CMD
  41145   41131 root     S    bash --norc

S or Ss is normal for an idle shell waiting for input. A CPU loop shows R while it runs.

A zombie (Z in STAT) is a terminated but unreaped process. It uses no CPU and no longer has its normal user-space address space, but it retains a PID and a small process-table entry until the parent collects its exit status with wait(). Killing the zombie PID does not work. Fix the parent — restart the service, fix the buggy parent, or reboot as a last resort.

Persistent D state usually means the process is stuck in kernel I/O. SIGKILL cannot be caught or ignored, but a process blocked in uninterruptible sleep may remain visible until the kernel I/O operation completes or fails. Persistent D processes often need filesystem, driver, or NFS troubleshooting rather than repeated kill -9.

top reports zombie count in the Tasks summary line (0 zombie on a healthy host).


Monitor and troubleshoot CPU and memory

Live sorting is where top command fits into the workflow. Interactive top refreshes continuously; batch mode prints one screen for logs and scripts.

top calculates per-process and CPU-state percentages between refreshes. Discard the first batch frame and read the second for a clearer troubleshooting sample:

bash
top -b -d 1 -n 2 -o %CPU | awk 'BEGIN { frame=0 } /^top -/ { frame++ } frame==2' | head -15
output
top - 18:48:42 up 1 day,  8:40,  4 users,  load average: 2.56, 1.52, 1.44
Tasks: 307 total,   5 running, 302 sleeping,   0 stopped,   0 zombie
%Cpu(s): 10.9 us,  3.9 sy,  0.0 ni, 54.1 id,  0.0 wa, 29.6 hi,  1.6 si,  0.0 st
MiB Mem :   6069.0 total,   1078.5 free,   3814.3 used,   1451.9 buff/cache
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  34559 root      20   0 5250536   2.0g  42116 S 102.1  33.5  52:07.23 hugo
  44698 root      20   0  228412   3196   2936 R  82.4   0.1   0:03.52 proc-la+
     53 root      39  19       0      0      0 S   1.1   0.0   0:11.93 khugepa+
  33929 root      20   0   11.3g  47708  14452 S   1.1   0.8   0:48.06 node
  44700 root      20   0  231596   5472   3364 R   1.1   0.1   0:00.03 top

Sort by resident memory when the symptom is RAM pressure:

bash
top -bn1 -o %MEM | head -15
output
top - 18:40:17 up 1 day,  8:32,  4 users,  load average: 1.82, 1.72, 1.59
Tasks: 290 total,   2 running, 288 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.9 us,  1.7 sy,  0.0 ni, 92.9 id,  0.0 wa,  3.0 hi,  0.6 si,  0.0 st
MiB Mem :   6069.0 total,   1097.9 free,   3800.4 used,   1446.7 buff/cache
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  34559 root      20   0 5249960   2.0g  42084 S   0.0  34.1  46:50.47 hugo

Inspect the PID at the top of the %MEM list — here 34559 (hugo):

bash
ps -p 34559 -o pid,user,stat,vsz,rss,pmem,etime,cmd
output
PID USER     STAT    VSZ   RSS %MEM     ELAPSED CMD
  34559 root     Sl+  5250856 2117860 34.0    54:46 hugo server --bind 0.0.0.0 --baseURL http://127.0.0.1:1313/ --port 1313

RES / RSS is resident physical memory. VIRT / VSZ is the process virtual address space — it includes mappings that are not currently in RAM, so a large VIRT alone does not mean the process is using that much physical memory.

Read the load average line as runnable tasks using or waiting for CPU plus tasks in uninterruptible sleep (commonly waiting for I/O). Load is not normalized by CPU count — compare it with nproc on the host:

bash
nproc
output
3

On a host where nproc returns 3, a load average near 3.0 represents roughly one runnable or uninterruptible task per available CPU on average. Interpret it together with CPU idle and I/O-wait values because load also includes tasks in D state. Linux load average counts runnable tasks and tasks waiting in uninterruptible sleep, not only the CPU run queue.

In interactive top, press P to sort by CPU and M to sort by memory. Adjust scheduler priority with nice or renice when you need to give a CPU-heavy process less favorable scheduling so competing processes receive more opportunity to run.

Use this sequence instead of jumping straight to kill -9:

  1. Confirm symptoms — slow shell, high load, or monitoring alert
  2. Identify candidatestop -bn1 -o %CPU or top -bn1 -o %MEM
  3. Inspect detailsps -p PID -o pid,ppid,user,stat,etime,cmd and memory columns when RAM is the concern
  4. Check service ownershipsystemctl status servicename when the command is a daemon child
  5. Send SIGTERMkill PID or systemctl stop servicename for unit-managed services
  6. Verify recoveryps -p PID, load average, and application health

Killing a child process that a supervisor immediately respawns fixes nothing until you stop the parent service or unit.


Manage foreground and background jobs

Shell jobs belong to your current bash session. They are not the same as every process on the system.

Start a long command in the background with &:

bash
sleep 300 &

The shell prints a job number and PID. List session jobs:

bash
jobs -l
output
[1]+ 41217 Running                 sleep 300 &

Bring a stopped or background job to the foreground with fg %1. Resume a stopped job in the background with bg %1. Press Ctrl+Z while a foreground job runs to stop it and return control to the shell — then use bg or fg.

nohup ignores hangups but does not put a command in the background by itself — the trailing & does that:

bash
nohup sleep 600 >/tmp/sleep-600.log 2>&1 &

disown removes a background job from the shell's job table without killing the process. System daemons started by systemd are outside this job table entirely.


Send signals to processes

Processes react to signals. The kill and pkill reference covers every signal name; this workflow uses the common ones first.

Signal Number Typical use
SIGTERM 15 Polite shutdown — default for kill
SIGKILL 9 Forced kill — cannot be caught or ignored
SIGHUP 1 Hangup — daemons often reload or exit

Stop a known PID with SIGTERM — replace 43175 with the PID you identified through ps, pgrep, or top:

bash
kill -TERM 43175

When you know the PID, prefer kill PID or kill -TERM PID over broad pkill -f patterns that can match multiple command lines. killall signals processes by executable name — use carefully on shared hosts because it matches every process with that name.

Verify the process exited:

bash
ps -p 43175 -o pid,tty,time,cmd
output
PID TT           TIME CMD

Only the header row remains when the PID no longer exists. Reserve kill -9 or pkill -9 for processes that ignore SIGTERM after you have identified the correct PID.

For a background job in your shell, run sleep 300 &, list with jobs -l, then kill %1 or kill the PID from jobs -l.


Process management quick reference

Task Command
Full process list ps aux or ps -ef
Preview name matches pgrep -af pattern
One PID details ps -p PID -o pid,ppid,user,stat,etime,cmd
Memory per PID ps -p PID -o pid,user,stat,vsz,rss,pmem,etime,cmd
Live CPU sort top then P, or second-frame top -b -d 1 -n 2 -o %CPU
Live memory sort top -bn1 -o %MEM
CPU count for load context nproc
Background job command &
List shell jobs jobs -l
Survive SSH logout nohup command >/path/log 2>&1 &
Polite stop kill PID or kill -TERM PID
Force stop (last resort) kill -9 PID
Process tree pstree -p or ps -ef --forest
Service-owned process systemctl status unit then systemctl stop unit

Troubleshooting

Symptom Likely cause Fix
High CPU, one PID in top Runaway user script or worker ps inspect; kill -TERM; verify; use -9 only if needed
Process returns immediately after kill Parent respawns child Stop systemd unit or parent supervisor, not only the child PID
Zombie listed in ps Parent not calling wait() Restart parent service or fix application; do not kill the zombie
D state will not die Stuck kernel I/O Diagnose storage/NFS/driver; SIGKILL may not help until I/O completes
Background job dies on SSH logout SIGHUP to session nohup … &, disown, or run under systemd
kill: Operation not permitted Different user or capability Run with sudo or signal as root

References


Summary

Linux process management is a chain: list with ps aux or ps -ef, confirm load and CPU or memory with top, read STAT and PPID before you signal anything, and stop services at the systemd unit when a supervisor keeps respawning workers.

Default to SIGTERM through kill, then verify with ps -p. Zombies are unreaped exit statuses — fix the parent. D state means kernel I/O may block even SIGKILL until the operation completes or fails. Shell jobs (jobs, fg, bg) apply only to your session; top normally displays system-wide tasks.

When CPU contention is chronic, inspect priority with ps -o ni and consider nice and renice to give a heavy process less favorable scheduling before you terminate production workloads.


Frequently Asked Questions

1. How do I find which process is using high CPU on Linux?

Run top or top -bn1 -o %CPU for a one-shot snapshot, note the PID and command, then inspect with ps -p PID -o pid,ppid,user,stat,etime,cmd. Send SIGTERM with kill PID before resorting to SIGKILL.

2. What is the difference between kill and kill -9?

kill sends SIGTERM by default, which asks the process to exit cleanly. kill -9 sends SIGKILL, which cannot be caught or ignored, but a task in uninterruptible D state may remain visible until kernel I/O completes or fails. Use SIGTERM first so daemons can flush data and release resources.

3. Can I kill a zombie process?

Killing a zombie PID does not help because the process has already exited. The kernel keeps the zombie until its parent reads the exit status with wait. Fix or restart the parent process, or restart the supervising service.

4. What is the difference between a shell job and a system process?

Shell jobs are commands started from your current bash session and listed with jobs. System processes include every task on the host. ps aux and ps -ef request a system-wide listing, while plain ps normally shows only processes associated with the invoking user and terminal. jobs lists only your session background tasks.

5. Why does my background job stop when I disconnect SSH?

The shell may send SIGHUP to child jobs when the session ends. Use nohup with trailing ampersand, disown, or systemd to keep long tasks running after logout. Shell jobs are tied to the terminal session that created them.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)