courses

Process Management

Seeing and controlling what is running

A process is a running instance of a program, and being able to list, inspect, signal, and prioritize processes is core to troubleshooting — and to security. The very same ps, ss, and /proc skills you use to find a runaway job are what you use to spot a malicious process or persistence mechanism on a compromised host, and the fork()/exec() model below is the foundation under memory-layout and exploitation concepts like ASLR.

This page covers job control (foreground/background), finding processes (ps, pgrep, top, /proc), signals and how to send them, killing processes safely, scheduling priority (nice), and the fork()/exec() process model.

Background and Foreground Processes

Running jobs in the foreground and background

By default, commands run in the foreground — the shell waits for them to finish.

# Run in foreground (shell blocks)
sleep 60

# Run in background by appending &
sleep 60 &
# [1] 12345   ← job number and PID

# Run a command, then send it to background
sleep 60        # start in foreground
# Press Ctrl-Z to suspend it
bg              # resume it in the background
fg              # bring the most recent background job to foreground
fg %2           # bring job number 2 to foreground

Job control commands

jobs            # list background and suspended jobs
jobs -l         # include PIDs
bg %1           # resume job 1 in background
fg %1           # bring job 1 to foreground
kill %1         # send TERM signal to job 1

Keeping jobs alive after logout

Background jobs tied to a terminal are killed when the session ends. Use one of the following to persist them:

nohup command &                    # redirect output to nohup.out, ignore SIGHUP
nohup long_script.sh > run.log &   # custom output file

disown %1                          # detach job 1 from shell (bash)
disown -a                          # detach all jobs

# screen and tmux provide persistent session multiplexing
tmux new -s mysession              # new named tmux session
tmux attach -t mysession           # reattach
screen -S mysession                # new named screen session
screen -r mysession                # reattach

Worked example — run a long job that survives logout:

nohup ./backup.sh > /var/log/backup.log 2>&1 &
echo "Backup PID: $!"

Listing and Finding Processes

ps — process snapshot

ps aux                      # all processes, BSD format (most common)
ps -ef                      # all processes, UNIX format
ps auxf                     # process forest (tree view)
ps aux | grep nginx         # find nginx processes
ps -u alice                 # processes owned by alice
ps -p 1234                  # specific PID
ps -eo pid,ppid,user,cmd    # custom output columns
ps -eo pid,user,cmd,pcpu,pmem --sort=-pcpu | head -20  # top CPU consumers

Common ps aux output columns:

Column Meaning
USER Process owner
PID Process ID
%CPU CPU usage
%MEM Memory usage
VSZ Virtual memory size (KB)
RSS Resident set size — physical RAM used (KB)
STAT Process state
START Start time
COMMAND Command line

Process state codes:

Code Meaning
R Running or runnable
S Sleeping (interruptible)
D Uninterruptible sleep (usually I/O wait)
Z Zombie — terminated but not yet reaped by parent
T Stopped (by signal or debugger)
s Session leader
+ Foreground process group

pgrep and pids

pgrep nginx                 # list PIDs matching name
pgrep -l nginx              # list PID and name
pgrep -u alice              # PIDs owned by alice
pgrep -a sshd               # full command line
pidof nginx                 # PIDs of a named process (exact match)

top and htop

top                         # interactive live view
top -p 1234,5678            # watch specific PIDs
htop                        # enhanced viewer (install with apt/dnf)

/proc filesystem

Each process has a directory under /proc/<PID>/:

ls /proc/1234/              # process directory
cat /proc/1234/cmdline      # full command line (NUL-separated)
cat /proc/1234/status       # process status info
cat /proc/1234/maps         # memory map
ls -la /proc/1234/fd/       # open file descriptors

Worked example — find what process owns a port:

ss -tlnp | grep :80
# or
lsof -i :80

Process Signals

Signals are software interrupts sent to processes. The kernel delivers them; processes can catch, ignore, or act on them.

Common signals

Signal Number Default action Description
SIGHUP 1 Terminate Hangup — reload config in many daemons
SIGINT 2 Terminate Interrupt from keyboard (Ctrl-C)
SIGQUIT 3 Core dump Quit from keyboard (Ctrl-\)
SIGKILL 9 Terminate Unconditional kill — cannot be caught or ignored
SIGTERM 15 Terminate Graceful termination request (default for kill)
SIGSTOP 19 Stop Pause process — cannot be caught or ignored
SIGCONT 18 Continue Resume a stopped process
SIGUSR1 10 Terminate User-defined signal 1
SIGUSR2 12 Terminate User-defined signal 2
SIGCHLD 17 Ignore Child process stopped or terminated
kill -l                     # list all signal names and numbers

Sending signals

kill PID                    # send SIGTERM (graceful stop) by default
kill -9 PID                 # send SIGKILL (force kill)
kill -SIGTERM PID           # explicit SIGTERM
kill -HUP PID               # send SIGHUP (often triggers config reload)
kill -0 PID                 # check if process exists (no signal sent)

# By name
pkill nginx                 # send SIGTERM to all processes named nginx
pkill -9 nginx              # force kill
pkill -HUP nginx            # reload

# By job number
kill %1                     # send SIGTERM to job 1

Keyboard shortcuts for signals

Shortcut Signal Effect
Ctrl-C SIGINT (2) Interrupt (stop) the process
Ctrl-Z SIGTSTP (20) Suspend (stop) the process
Ctrl-\ SIGQUIT (3) Quit with core dump

Killing Processes

kill and killall

kill 1234                   # SIGTERM to PID 1234
kill -9 1234                # SIGKILL to PID 1234 (last resort)
kill -SIGTERM 1234          # explicit signal name

killall nginx               # SIGTERM to all processes named nginx
killall -9 nginx            # SIGKILL to all
killall -u alice            # kill all of alice's processes
killall -i nginx            # interactive: confirm each kill

pkill

pkill -f "python script.py"   # match against full command line
pkill -u alice                # all processes owned by alice
pkill -t pts/1                # all processes on terminal pts/1

Handling zombie processes

A zombie (Z state) is a process that has exited but whose parent has not called wait() to collect its exit status. Zombies cannot be killed — they must be reaped by the parent.

ps aux | grep Z              # find zombie processes
# Get the parent PID (PPID) of the zombie
ps -o ppid= -p <zombie_pid>
# Send SIGCHLD to the parent to trigger reaping
kill -CHLD <parent_pid>
# If parent is broken, killing it will cause init to adopt and reap zombies

Worked example — clean up a stuck process gracefully:

# 1. Try graceful stop first
kill -TERM $(pgrep myapp)
sleep 5

# 2. Check if it's still running
pgrep myapp && echo "Still running"

# 3. Force kill only if necessary
kill -9 $(pgrep myapp)

⚠️ Reach for SIGTERM (15) before SIGKILL (9). SIGTERM lets a process clean up — flush buffers, close files, remove lock files. SIGKILL cannot be caught and gives the process no chance to clean up, which can corrupt data or leave stale locks. Try kill PID, wait, and only escalate to kill -9 if it ignores you.

Process Priorities

Linux uses a scheduling priority called “nice value” ranging from -20 (highest priority) to 19 (lowest). Only root can set negative nice values.

nice — start a process with a given priority

nice command                  # start with niceness +10 (lower priority)
nice -n 10 command            # explicit niceness adjustment
nice -n -5 command            # higher priority (requires root)
nice -n 19 ./backup.sh &      # run backup at lowest priority

renice — change priority of a running process

renice 10 -p 1234             # set niceness to 10 for PID 1234
renice -n 5 -p 1234           # relative: increase niceness by 5
renice 15 -u alice            # set all of alice's processes to 15
renice -n -5 -p 1234          # increase priority (requires root)

Viewing priorities

ps -eo pid,ni,cmd | head -20   # show nice values (NI column)
top                            # NI column shows nice value

Real-time scheduling with chrt

chrt -p 1234                   # show scheduling policy for PID
chrt --fifo 50 command         # run with FIFO real-time policy, priority 50
chrt --rr 50 command           # round-robin real-time policy

Worked example — run a CPU-intensive job without affecting other users:

nice -n 19 tar -czf backup.tar.gz /home/ &

Process Forking

How processes are created

Linux uses two system calls for process creation:

The typical pattern: the shell fork()s a child, the child calls exec() to run the requested program, and the parent wait()s for the child to finish.

Process relationships

ps -ejH                        # process tree with PGID and SID
ps axf                         # ASCII art process forest
pstree                         # tree of processes by name
pstree -p                      # include PIDs
pstree -u                      # include usernames

Process hierarchy concepts

Term Meaning
PID Process ID — unique identifier
PPID Parent Process ID
PGID Process Group ID — used for job control
SID Session ID — processes sharing a controlling terminal
init / systemd PID 1 — ancestor of all user processes

Viewing parent-child relationships

# Show PID and PPID
ps -eo pid,ppid,cmd | head -20

# Find parent of a specific PID
ps -o ppid= -p 1234

# Follow the ancestry chain
cat /proc/1234/status | grep -E "Pid|PPid"

Copy-on-write

When fork() is called, Linux uses copy-on-write (COW): the child shares the parent’s memory pages until either process modifies them, at which point a private copy is made. This makes fork() very fast and memory-efficient.

Worked example — observe a shell fork:

# This bash script forks a subshell
(echo "I am the child, PID $$"; sleep 2) &
echo "I am the parent, PID $$, child job running"
wait
echo "Child has exited"

Key takeaways

References


Related course pages: Shell and Other Basics · Navigation Basics · Host Security · Memory Corruption · SIEM and SOC

🛠️ Maintenance note: process tooling is stable, but note the course VM uses systemd as PID 1 (so long-running services are better managed as units than with nohup), and ss has fully replaced the deprecated netstat — verify both on the installed image.