Linux / Basic: command-line foundations / Lesson 5
Follow processes, jobs, and signals
Understand process lifetimes, inspect selected processes, and distinguish graceful termination from forced termination.
On this page
Lesson goals
- Connect parent processes, PIDs, and exit status.
- Distinguish a shell job from an operating-system process.
- Choose signals based on documented behavior.
Processes have identities and lifetimes
A process is a running program with credentials, open descriptors, memory mappings, and a current directory. A process ID identifies it only during a lifetime; the kernel may later reuse that number. A parent starts children and collects their completion status with wait. A program can replace its executable image through exec without changing its process ID. Threads share much of a process’s state while executing independently. These distinctions explain why a single command can create several processes and why a name alone is an unreliable target.
Inspect selected processes
ps presents a snapshot rather than continuous truth. Choose explicit columns such as PID, PPID, state, and command; inspect the owning user before acting. A sleeping state is usually normal waiting, not proof of a hung process. A zombie has exited but its parent has not yet collected the status, so repeatedly sending it a kill signal cannot help. /proc exposes current process information when permissions allow. Arguments and environments can contain secrets, so avoid publishing unfiltered process listings or collecting another user’s details unnecessarily.
sleep 1 &
child=$!
ps -p "$child" -o pid=,ppid=,stat=,comm=
wait "$child"
printf 'completed=%s\n' "$?"
The PID values vary. The example inspects only its new sleep process and waits for it; normal completion prints completed=0.
Job control belongs to the interactive shell
An interactive Bash job usually corresponds to a pipeline and its process group. A trailing & starts work asynchronously, and $! records the most recent background process identifier. jobs lists the current shell’s jobs, not every process in the system. Ctrl-Z normally requests terminal suspension; bg resumes a stopped job in the background, and fg gives a job the terminal again. A stopped job is not complete. Noninteractive scripts should manage child PIDs and wait explicitly rather than assume interactive job numbering is available.
Signals request specific actions
kill sends a signal; its name does not mean every invocation immediately destroys a process. SIGTERM requests termination and permits a program to clean up if it has installed a handler. SIGINT usually comes from Ctrl-C to the foreground process group. SIGKILL terminates without allowing a handler, while SIGSTOP cannot be handled either. Prefer symbolic names because some signal numbers vary by architecture. Before signaling, confirm ownership, current identity, and the intended scope. Broad name matching can affect unrelated processes or a newly reused PID.
bash -c 'trap "echo term-observed; exit 0" TERM; kill -s TERM "$$"'
term-observed
The inner Bash installs a handler and sends TERM to itself. No unrelated PID is targeted. The trap prints a message and exits successfully.
Background work needs a lifetime policy
Putting & after a command does not make it a reliable service. Terminal closure, session policies, inherited descriptors, and unhandled signals can still end it. nohup changes hangup handling but does not provide startup ordering, health checks, or restart supervision. For recurring or long-lived workloads, a service manager or scheduler is a better model. In a small script, record each child, wait for completion, and propagate failures. If you cancel work, allow bounded cleanup time and document what temporary data may remain.
Common errors and fixes
Signaling by a guessed PID
- Why it happens
- A stale listing or copied number identifies the wrong lifetime.
- How to fix it
- Recheck the selected process and its owner before acting.
- Prevent it next time
- Capture children directly with $! and keep their scope narrow.
Using SIGKILL first
- Why it happens
- Forced termination was mistaken for normal shutdown.
- How to fix it
- Prefer a documented graceful mechanism and bounded waiting.
- Prevent it next time
- Learn cleanup and shutdown requirements before cancellation.
Treating a stopped job as finished
- Why it happens
- Ctrl-Z suspended execution instead of terminating it.
- How to fix it
- Inspect jobs and deliberately resume or terminate the lab job.
- Prevent it next time
- Distinguish stopped, running, and exited states.
Check your understanding
1. Why can a zombie remain after SIGKILL?
Show worked solution
Its executable work already ended. Only the small record needed for the parent’s wait remains. The remedy is to diagnose why the parent is not reaping children; repeatedly signaling the exited child cannot make it perform cleanup or collect itself.
2. A script starts two children and exits immediately. Describe a more reliable completion contract.
Show worked solution
Capture both child PIDs immediately, wait for each, record each result, and return a documented failure if either required task fails. Decide how cancellation reaches both children and what cleanup is required. Background startup alone cannot certify completion.
Key takeaways
- Inspect process identity and scope before sending a signal.
- Use wait to turn child completion into a result.