builds / shell / stage-6SHEET 6 / 6 · REV ASIGN IN
ASSEMBLY DIAGRAM — YOUR SHELLSCALE: LEARNING
keyboardthe outside worldCOMMAND LOOP✓ builtPROCESS LAUNCHER✓ builtFD PATCH PANEL✓ builtPIPE MANIFOLD✓ builtSIGNAL BUS✓ builtJOB BOARD⚙ building
BUILTUNDER CONSTRUCTIONNOT YET IMAGINED INTO EXISTENCE
STAGE 6 · THE JOB BOARD

The Job Board

Everything you built becomes a table of jobs and one terminal to lend out.

What you're wiring up

Job control is the payoff. The process groups from Stage 5 become jobs the shell tracks in a table. The terminal becomes a token the shell lends to exactly one job at a time. SIGCHLD becomes the news feed that keeps the table honest about who is still alive.

The state you have not met yet is stopped. SIGTSTP parks a process mid-instruction and SIGCONT resumes it, and it is a real kernel state, not a shell fiction: ps will show the process as T. Once you have implemented fg, the difference between a stopped process, a background process, and a zombie is knowledge you own for every debugging session for the rest of your career.

One rule constrains the design. You cannot print from a signal handler; it is undefined behavior. So the handler only records what happened, and the Done notices are printed at prompt time, which is also why real shells never interrupt a half-typed line to tell you a background job finished.

Assembly steps

[ 01 ]
Background launch: a trailing & skips the wait, prints [1] with the pid, and records the job (id, pgid, command, state) in a job table.
hint

Background jobs never get the terminal, so no tcsetpgrp for them.

[ 02 ]
Reap asynchronously with a SIGCHLD handler or a WNOHANG sweep before each prompt, printing [1] Done <cmd> just before the next prompt.
hint

In the handler, only set a flag or loop waitpid(-1, ..., WNOHANG|WUNTRACED). Do all printing at prompt time.

[ 03 ]
Handle Ctrl-Z: a foreground job that stops goes into the table as STOPPED, the shell reclaims the terminal and prompts.
hint

You need WUNTRACED in the foreground waitpid, or a stop looks to your shell like nothing happened at all.

[ 04 ]
Implement jobs, fg %n (give the terminal, SIGCONT, wait) and bg %n (SIGCONT, do not wait).
hint

Signal the whole group with kill(-pgid, SIGCONT). The minus sign is the entire difference.

[ 05 ]
Exit hygiene: exit with stopped jobs warns once with There are stopped jobs, and exits for real on the second attempt.
hint

One boolean warned flag, reset by any other command. That is exactly what bash does.

Go deeper (after it passes)

The schematic is fully inked. Victory lap: run your shell as your login shell for an hour and notice everything it cannot do yet. The job lifecycle of Running, Stopped and Done is a genuine state machine, so compare your enum and switch against HFDP M9 — The Gumball State Machine. Then take the process model somewhere new: build-your-own-redis spawns and pipes clients exactly the way you just learned.