The process hierarchy, and hunting a runaway job
◈ 10 cardsEvery process has a parent, from PID 1 down to your shell — and a four-stage pipeline that finds a named process and kills it.
Every process has a parent except the first
Processes form a tree, because fork() is the only way to make one and a fork always has a caller. Follow any process's PPID chain upwards and you arrive at the same place.
At boot, the kernel builds the very first process from nothing — it cannot fork, because there is nothing yet to fork from. It has PID 0 and no parent, and it is the only process ever created that way. It then creates the kernel service processes and, crucially, init — PID 1, the ancestor of every user process on the system.
init runs the system startup scripts, and then, on a classical UNIX, consults a table of active terminal lines and forks a getty for each one. getty sets the line's terminal attributes and prints login:. When you type a name, getty arranges for login to run, login prompts for the password and checks it, and on success login execs your login shell.
That last word is the point of the whole story. login does not fork your shell — it execs it, so your shell is the very same process that was login a moment earlier, and was getty before that. There is no extra process in the chain. And that is why exiting your shell ends the session and returns you to a login: prompt: the process you were talking to has terminated, so init notices and starts a fresh getty on the line. Two system calls you learned in the first lesson of this module explain the entire login sequence.
Over SSH the shape is identical and the names are not. A listening sshd forks a private sshd for your connection; that one authenticates you, allocates a pseudo-terminal (pts/0), and runs your login shell on it. Ctrl-D ends the shell, which ends your private sshd, which ends the connection — the same causal chain, one layer over.
Worked example — building the hunting pipeline
Somebody left sleep 1000 running on a shared machine and you want every copy of it gone. Build the pipeline one stage at a time and look at the output after each.
Stage 1 — get every process with its PID:
ps -ef
Stage 2 — narrow it. The obvious spelling has a famous flaw:
ps -ef | grep 'sleep 1000'
This prints your target and one extra line: the grep itself. Both sides of a pipeline start at the same moment, so grep is already running and visible when ps takes its snapshot — and grep's own command line contains the string it was asked to look for, so it matches itself. The fix is the bracket trick:
ps -ef | grep '[s]leep 1000'
[s] is a regular-expression character class matching exactly one character, s, so the pattern still matches the text sleep 1000. But grep's command line now literally reads grep [s]leep 1000, which the pattern does not match, because there are square brackets in it. The regex and the string it is written as have been made to differ. It costs two characters and it removes the false positive completely.
Stage 3 — keep only the PID. In ps -ef the columns are UID PID PPID …, so the PID is field 2:
ps -ef | grep '[s]leep 1000' | awk '{print $2}'
Stage 4 — turn those numbers into arguments for kill:
ps -ef | grep '[s]leep 1000' | awk '{print $2}' | xargs kill
xargs reads words from its standard input and appends them to the command line of the program you name. That is the whole job, and it is necessary because kill reads its targets from its arguments and never from standard input — a bare | kill sits there with nothing to do.
The equivalent with command substitution reads more directly and is worth knowing too:
kill $(ps -ef | grep '[s]leep 1000' | awk '{print $2}')
One caution: if the pipeline finds nothing, xargs kill runs kill with no arguments and you get a usage message, while kill $(…) does the same. xargs -r kill on Linux skips the run entirely when the input is empty.
And go up the escalation ladder, not down it. Try the pipeline as written first — it sends SIGTERM and the process gets to clean up. Only if a process ignores that should you reach for xargs kill -9, which it cannot ignore and after which it cleans up nothing.
On Linux, pgrep -f 'sleep 1000' and pkill -f 'sleep 1000' do stages 2 and 3 in one step, and never match themselves. They are the practical answer; the pipeline is the one that shows you understand what each stage does, which is what an exam is asking.