Lion Cipher Lab
Write a Unix shell from scratch (fork, exec, pipes, and redirection)
- Foundations
- Advanced
- ~75 min
- Free · no account required
Why it matters
A shell feels like magic until you write one, and then it becomes the most demystifying program you will ever build. Every command you have ever typed — `ls`, `grep`, `python`, a pipeline, a redirect — is the shell making a tiny handful of syscalls on your behalf: fork to make a new process, exec to become the program you asked for, wait to collect its exit status, pipe and dup2 to wire programs together, open to redirect a file, and signal handling so Ctrl-C hits the right process. Those same syscalls are the foundation the rest of the corpus stands on: strace exists to SHOW you the…
What you'll learn
- the read-eval-print loop of a shell: read a line, tokenize it into an argv array, dispatch
- the fork/exec/wait model: fork(2) duplicates the process, execvp(3) replaces the child's image, waitpid(2) reaps it
- why exec never returns on success — it overwrites the calling program, so the line after execvp only runs on failure
- why built-ins like cd and exit MUST run in the parent shell, and cannot be a forked child
- I/O redirection with open(2) + dup2(2): point fd 0 or fd 1 at a file, then close the original descriptor
- building a pipeline a | b with pipe(2) + dup2, and the mandatory close of BOTH pipe ends in BOTH processes
- the classic pipe deadlock: a reader that never sees EOF because some process still holds the write end open
- zombie processes: why a child that has exited but not been wait()ed shows STAT Z (defunct) in ps
- signal handling with signal(2)/sigaction: the shell ignores SIGINT so Ctrl-C kills the foreground child, not your shell
- the judgment: fork-then-exec vs posix_spawn, execvp (PATH search) vs execve, and job control / process groups as the next rung
Steps in this lab
- What a shell actually is: the fork / exec / wait loop
- Set up a scratch box and the skeleton
- The REPL core: fork + execvp + waitpid + two built-ins
- Why cd and exit MUST be built-ins (and cannot be forked)
- I/O redirection: open + dup2, and closing the original fd
- The pipeline a | b: pipe + dup2, and closing BOTH ends everywhere
- Signals: why the shell ignores Ctrl-C but the child doesn't
- Break it on purpose #1: create a zombie (forget to wait)
- Break it on purpose #2: the classic pipe deadlock (fd leak)
- The judgment: where your shell sits, and the next rungs