Process Management
- 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) beforeSIGKILL(9).SIGTERMlets a process clean up — flush buffers, close files, remove lock files.SIGKILLcannot be caught and gives the process no chance to clean up, which can corrupt data or leave stale locks. Trykill PID, wait, and only escalate tokill -9if 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:
fork()— creates an exact copy of the calling process (the child). Both parent and child continue executing from the same point.exec()— replaces the current process image with a new program. The PID stays the same.
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
- Job control runs work in the foreground or background:
&to background,Ctrl-Z+bgto suspend-and-detach,fgto recall, andnohup/disown/tmuxto survive logout. - Find processes with
ps aux/ps -ef,pgrep/pidof, live viewstop/htop, and the per-process/proc/<PID>/tree (cmdline,status,maps,fd/) — the same toolkit used to spot suspicious processes. - Signals are software interrupts:
SIGTERM(15) asks nicely and is the default;SIGKILL(9) andSIGSTOP(19) cannot be caught;SIGHUPoften triggers a config reload. Send withkill/pkill/killall. - Nice values (-20 high … 19 low) set scheduling priority; only root raises priority (negative niceness).
- Processes are created by
fork()(copy, via copy-on-write) +exec()(replace image); PID 1 (systemd/init) is the ancestor of all and reaps orphaned zombies.
References
- W. Shotts, The Linux Command Line — processes chapter. https://linuxcommand.org/tlcl.php
ps(1),kill(1),nice(1),pstree(1)man pages. https://man7.org/linux/man-pages/man1/ps.1.htmlsignal(7)— overview of Linux signals. https://man7.org/linux/man-pages/man7/signal.7.htmlproc(5)— the/procfilesystem. https://man7.org/linux/man-pages/man5/proc.5.html
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), andsshas fully replaced the deprecatednetstat— verify both on the installed image.