Job control, terminal signals, and surviving logout
◈ 13 cardsSuspend, background and foreground a job; what each terminal control key really sends; and why nohup and & are two different mechanisms.
Signals are software interrupts
A signal is a short asynchronous notification delivered to a process by the kernel — because of a terminal key, a kill command, a hardware fault, or a timer. It carries no data beyond its own identity. On receipt a process does one of three things: take the kernel's default action (for most signals, terminate), ignore it, or catch it by having installed a handler function that runs instead.
Learn signals by name, never by number. The numbers differ between systems — SIGTSTP is 18 on FreeBSD and Solaris but 20 on Linux; SIGCHLD is 20 there and 17 on Linux — and your assignments run on Linux. kill -TERM 4821 is portable and self-documenting; kill -15 4821 is neither. kill -l lists what your system has.
The names worth knowing: SIGHUP (hang-up — sent to your session's processes when you log out), SIGINT (interrupt), SIGQUIT (quit, with a core dump), SIGKILL (sure kill), SIGTERM (polite termination — kill's default), SIGSTOP and SIGTSTP (stop a process), SIGCONT (resume it), SIGCHLD (a child changed state).
SIGKILL and SIGSTOP cannot be caught, blocked or ignored. That is a kernel guarantee, and it is deliberate: it means there is always a way to stop a process no matter what its author wrote. It is also why kill -9 is a last resort. A process killed with SIGTERM runs its handler — flushing buffers, removing its lock file, closing its database connection. A process killed with SIGKILL runs nothing, so the temporary files, the lock file and the shared-memory segment are all left behind for you to clean up by hand.
The three terminal keys, and the one that is not a signal
Your terminal driver turns three keystrokes into signals aimed at the foreground process group:
Ctrl-CsendsSIGINT. Default action: terminate.Ctrl-\sendsSIGQUIT. Default action: terminate and write a core dump.Ctrl-ZsendsSIGTSTP. Default action: stop the process — stateTinps. It is suspended, not terminated, andfgresumes it exactly where it was.
Ctrl-D sends no signal at all. It is the terminal's end-of-file character. Pressing it on an empty line makes the pending read() return zero bytes, which every well-written reader interprets as no more input. That is why Ctrl-D ends cat > notes, ends a mail message, ends bc — and ends your shell, because a shell is a loop that reads a command and runs it, and a loop that gets EOF instead of a command exits normally.
This is the exam trap, so state the consequence plainly: Ctrl-C does not end an interactive shell. The shell catches or ignores SIGINT at its prompt precisely so that interrupting a runaway command does not also destroy your session; all Ctrl-C does at a prompt is abandon the half-typed line. The key that reliably ends a shell is the one that is not a signal.
Worked example — suspend, compile, resume
You are editing lab8.c in vim and want to compile without losing your place.
- Press
Ctrl-Z.vimreceivesSIGTSTPand stops. The shell prints[1]+ Stopped vim lab8.cand gives you a prompt.psnow showsvimwithSTATofT. - Run
gcc -o lab8 lab8.c. Fix nothing yet; just read the errors. - Run
fg %1. The shell sendsSIGCONT, handsvimthe terminal back, and you are on the same line of the same file with your undo history intact.
The job specifiers: %1 is job 1, %+ (or bare fg) is the current job, %- the previous one, %vim the job whose command starts with vim, %?lab8 the job whose command contains lab8. jobs -l lists them with PIDs so you can cross-reference against ps.
bg %1 would resume vim in the background instead — where it immediately stops again the moment it tries to read the keyboard, because a background process that reads from the terminal is sent SIGTTIN. Interactive programs belong in the foreground; that is what & is bad at.
Worked example — escalating a kill
A program has stopped responding. Escalate, and watch the message the shell prints when it reaps the job — each signal reports differently, which is how you know which one landed:
kill 4821— sendsSIGTERM, the default. The process gets a chance to clean up. The shell reportsTerminated.kill -INT 4821— sendsSIGINT, the same thingCtrl-Cwould have sent. Reported asInterrupt.kill -QUIT 4821—SIGQUIT, and a core file if core dumps are enabled. Reported asQuit.kill -KILL 4821— the sure kill. Uncatchable, so it always works, and the process runs no cleanup whatsoever. Reported asKilled.
Go down that list in order. If step 1 works you keep a clean temporary directory; if you start at step 4 you never find out whether step 1 would have.
kill accepts job IDs as well as PIDs — kill %1 — and only the superuser may signal another user's processes.
&, nohup, and what actually survives logout
These are two different mechanisms and the exam question turns on the difference.
&tells the shell not towait(). The job runs in the background and you get your prompt back. That is all it does. The job is still in your session, and when you log out it still receivesSIGHUP— and the default action forSIGHUPis to terminate.nohup cmdstartscmdwithSIGHUPset to ignored, and that disposition is inherited across theexec. Logout still sends the signal; the process now discards it.
So neither one alone is the answer. & without nohup dies at logout. nohup without & runs in the foreground and holds your terminal, so you cannot log out in the first place without suspending or killing it. You need both:
nohup ./crunch.sh > run.log 2>&1 &
If you do not redirect, nohup appends both streams to a file called nohup.out in the current directory (or in your home directory if the current one is not writable), and tells you so.
Two further points. First, nohup protects against SIGHUP and nothing else — the job is still perfectly mortal to kill, kill -INT, or kill -9. Second, nohup takes a command, not a command line with ; in it: in nohup a ; b, only a runs under nohup and b is an ordinary command the shell runs afterwards. Wrap the sequence in a script, or in nohup sh -c 'a; b' &.
Modern alternatives worth naming in an answer: setsid cmd & puts the job in a brand-new session with no controlling terminal, so no SIGHUP is ever aimed at it; disown -h %1 removes an already-running job from the shell's list so the shell will not signal it at exit; and tmux or screen keep a whole shell alive that you detach from and reattach to later, which is what most people actually use. A long-lived service belongs in a systemd user unit rather than in any of these.