Process and Job Management¶
Foreground, background, and job control¶
long_task.sh # runs in foreground — shell blocks until it exits
long_task.sh & # runs in background — shell prompt returns immediately
jobs # list background jobs for this shell session
fg %1 # bring job 1 back to the foreground
bg %1 # resume a stopped job 1 in the background
Ctrl+Z doesn't kill a foreground process — it sends SIGTSTP,
suspending it (paused, not terminated), moving it into the
background job list in a stopped state; bg resumes it there, fg
brings it back to the foreground. This is genuinely different from
Ctrl+C (SIGINT), which the process can catch and act on (or which
terminates it if uncaught) — suspension via SIGTSTP doesn't give the
process a choice or a chance to run cleanup code, it's paused by the
kernel directly.
Signals from the shell's perspective¶
This is the practical, terminal-driven view of
signals already
covered conceptually — Ctrl+C/Ctrl+Z are just keyboard shortcuts
for sending specific signals to whatever's running in the foreground.
SIGHUP ("hang up") is the one that surprises people running a
long process over SSH: closing the terminal or losing the SSH
connection sends SIGHUP to every process still attached to that
session, and the default action for SIGHUP is termination — a
long-running script left running in a plain foreground shell over
SSH dies the moment the connection drops, even though nothing
explicitly told it to stop.
Surviving terminal disconnection: nohup, disown, and real alternatives¶
nohup long_task.sh & # ignore SIGHUP specifically, keep running after logout
disown %1 # remove an already-backgrounded job from the shell's
# job table, so the shell exiting doesn't send it SIGHUP
nohup runs a command with SIGHUP explicitly set to be ignored —
the process survives the terminal closing. disown does something
related but distinct: it removes a job the shell is already tracking
from that shell's job table, so when the shell itself exits, it no
longer considers that job "its" to signal at all. For anything that
should be a genuinely durable background service (not just surviving
one terminal session) rather than a one-off long task, a proper
service manager (systemd) — with defined restart policies, log
capture, and start-on-boot behavior — is the more robust tool than
relying on nohup/disown tricks at the shell level.
Inspecting and killing processes¶
ps aux | grep myapp # list processes, filter by name
top # live view of CPU/memory usage per process
kill -TERM <pid> # ask nicely (SIGTERM, the default signal for `kill`)
kill -9 <pid> # SIGKILL — immediate, uncatchable, no cleanup
pkill -f "myapp" # kill by matching the command line, not just a known PID
kill (without -9) sends SIGTERM by default — a request to
terminate that a well-behaved process can catch and use to clean up
(closing files, finishing in-flight work) before exiting, exactly the
graceful-shutdown pattern
already covered. kill -9 (SIGKILL) skips all of that — the kernel
terminates the process immediately with no opportunity for it to run
any cleanup code at all, which is why it's a last resort (a process
truly stuck ignoring SIGTERM) rather than a routine way to stop
something.
Common pitfall¶
Using kill -9 as the default/first instinct to stop a stuck process,
rather than trying SIGTERM first, means any in-flight work that
process was doing — a half-written file, an open database transaction,
a partially-flushed buffer — is abandoned mid-operation with zero
chance to clean up, potentially leaving corrupted state behind that a
graceful shutdown would have avoided. Reserve -9 for the specific
case where SIGTERM was already tried and the process is confirmed to
be ignoring or unable to respond to it, not as a routine substitute
for it.