<!--date=20250207 -->


<h1>Process Management</h1>
<p>Understanding processes separates people who can debug a stuck system from people who just reboot it. Everything running on a Unix box is a process — it has a PID, an owner, a parent, a working directory, and a set of open file descriptors. Once you can see that structure clearly, a lot of mysterious system behaviour starts making sense.</p>

<h2>What's Running</h2>
<pre>
$ ps aux             # all processes, BSD-style flags (most commonly used)
$ ps -ef             # all processes, POSIX-style
$ ps aux | grep nginx   # find processes matching a name
$ pgrep -l nginx        # cleaner: list PIDs and names matching "nginx"
$ pidof nginx           # just the PIDs
</pre>

<h3>Process State Codes</h3>
<p>The STAT column in <code>ps aux</code> output tells you what the process is doing:</p>

<table>
  <thead><tr><th>Code</th><th>State</th><th>Notes</th></tr></thead>
  <tbody>
    <tr><td>R</td><td>Running or runnable</td><td>Actively using or waiting for CPU</td></tr>
    <tr><td>S</td><td>Sleeping</td><td>Waiting for I/O, a signal, or a timer — normal</td></tr>
    <tr><td>D</td><td>Uninterruptible sleep</td><td>Waiting on I/O — cannot be killed; usually a disk or NFS problem</td></tr>
    <tr><td>Z</td><td>Zombie</td><td>Finished but parent hasn't collected its exit status</td></tr>
    <tr><td>T</td><td>Stopped</td><td>Paused (SIGSTOP or Ctrl+Z)</td></tr>
  </tbody>
</table>

<p>Processes in state D (uninterruptible sleep) are typically stuck on a dying disk or an unresponsive network filesystem. <code>kill -9</code> won't work on them — you often have to reboot.</p>

<h2>Process Hierarchy</h2>
<pre>
$ pstree -p              # full process tree with PIDs
$ ps -o pid,ppid,cmd     # show PID and parent PID for every process
</pre>
<p>Every process has a parent. PID 1 (systemd or init) is the ancestor of everything. When you run a command from a shell, the shell forks a child to run it. Understanding parent-child relationships helps when you're trying to figure out why a process keeps restarting — something is respawning it.</p>

<h2>Signals</h2>
<p>Signals are how you communicate with a running process. Most people only know <code>kill -9</code>, which is the sledgehammer — it works but the process has no chance to clean up:</p>

<table>
  <thead><tr><th>Signal</th><th>Number</th><th>Meaning</th><th>Can be caught?</th></tr></thead>
  <tbody>
    <tr><td>SIGTERM</td><td>15</td><td>Polite termination — process can handle it and clean up</td><td>Yes</td></tr>
    <tr><td>SIGKILL</td><td>9</td><td>Force kill — cannot be caught or ignored</td><td>No</td></tr>
    <tr><td>SIGHUP</td><td>1</td><td>Hangup — daemons traditionally reload config on SIGHUP</td><td>Yes</td></tr>
    <tr><td>SIGSTOP</td><td>19/17</td><td>Pause a process (like Ctrl+Z)</td><td>No</td></tr>
    <tr><td>SIGCONT</td><td>18/19</td><td>Resume a paused process</td><td>Yes</td></tr>
    <tr><td>SIGINT</td><td>2</td><td>Interrupt (Ctrl+C in terminal)</td><td>Yes</td></tr>
  </tbody>
</table>

<pre>
$ kill -SIGTERM 1234    # or: kill -15 1234
$ kill -SIGKILL 1234    # or: kill -9 1234
$ kill -SIGHUP 1234     # reload config (nginx, apache, etc.)

$ pkill nginx           # kill by name
$ pkill -HUP nginx      # send HUP to all processes named nginx
$ killall firefox       # kill all processes with this exact name
</pre>

<p>Best practice: try SIGTERM first, wait a few seconds, then SIGKILL if the process doesn't exit. Most well-written daemons handle SIGTERM gracefully.</p>

<h2>Job Control</h2>
<p>Job control lets you manage multiple things in a single shell session:</p>
<pre>
$ long_command &        # run immediately in background
$ long_command
^Z                      # Ctrl+Z suspends the foreground process
$ bg                    # continue suspended job in background
$ fg                    # bring the most recent background job to foreground
$ fg %2                 # bring job 2 to foreground specifically
$ jobs                  # list all jobs in current session
</pre>

<pre>
# Survive session logout:
$ nohup ./myscript.sh &         # immune to hangup; output goes to nohup.out
$ disown %1                     # detach job 1 from the shell (no nohup file)

# Better: use tmux or screen for long-running sessions
$ tmux new -s mysession         # start a named tmux session
$ tmux attach -t mysession      # reattach after disconnecting
</pre>

<h2>Finding What's Using a Resource</h2>
<pre>
# What's using a port?
$ lsof -i :8080
$ ss -tlnp | grep 8080

# What process has this file open?
$ lsof /var/log/app.log

# What's blocking an unmount?
$ fuser /mnt/usb               # shows PIDs using the mount
$ fuser -k /mnt/usb            # kill all processes using it

# What files does a process have open?
$ lsof -p 1234
</pre>

<h2>Process Priority: nice and renice</h2>
<p>Nice values range from -20 (highest priority, hogs CPU) to 19 (lowest, yields to everything). Regular users can only lower priority (raise the nice value). Root can raise priority:</p>
<pre>
$ nice -n 10 ./heavy_job.sh      # start with lower priority (nice 10)
$ renice +10 -p 1234             # lower priority of a running process
$ renice -5 -p 1234              # raise priority (needs root)
$ renice +15 $(pgrep ffmpeg)     # lower priority of all ffmpeg processes
</pre>

<h2>Monitoring Over Time</h2>

<table>
  <thead><tr><th>Command</th><th>Shows</th><th>Notes</th></tr></thead>
  <tbody>
    <tr><td><code>top</code></td><td>Live CPU/memory by process</td><td>Press 1 to see per-CPU, M to sort by memory</td></tr>
    <tr><td><code>htop</code></td><td>Same but friendlier</td><td>Tree view, mouse support, colour coding</td></tr>
    <tr><td><code>vmstat 1</code></td><td>System-wide stats every second</td><td>Good for spotting memory pressure (swap activity)</td></tr>
    <tr><td><code>iostat -x 1</code></td><td>Per-device I/O stats every second</td><td>%util column shows if a disk is saturated</td></tr>
    <tr><td><code>sar -u 1 10</code></td><td>CPU usage, 10 samples</td><td>Part of sysstat package</td></tr>
  </tbody>
</table>

<p>When a system is slow, the sequence: check <code>top</code> for CPU/memory hogs → check <code>iostat</code> to see if it's I/O-bound → check <code>lsof</code> if a specific process seems to be holding resources.</p>

<h2>References</h2>
<ul>
  <li><a href="https://man.openbsd.org/ps.1" target="_blank" rel="noopener">ps(1) man page ↗</a></li>
  <li><a href="https://man7.org/linux/man-pages/man7/signal.7.html" target="_blank" rel="noopener">Linux signal(7) man page ↗</a></li>
  <li><a href="https://www.brendangregg.com/linuxperf.html" target="_blank" rel="noopener">Linux Performance — Brendan Gregg ↗</a></li>
</ul>
