Prep: fork, the User Shell, and Your Commands — 51k · 52k · 53k¶
Session: Fri Dec 4, 1h30 · Exercises: 51k_fork_wait · 52k_userland · 53k_ship_your_commands · Prep time: ~45 min · Lecture: exec as a System Call, and Userland (and exec, File Descriptors, and fork §4–§5)
What you will build¶
The finish line. First the kernel runs a tree of processes: fork copies the caller, exit parks the child as a zombie holding its status, and wait reaps it. Then exec becomes a system call and the shell leaves the kernel: run sh at the rv6$ prompt drops you into $, an unprivileged program that reaches the kernel only through ecall. Finally oslings ship compiles the five commands you wrote in September for RISC-V, flattens each ELF, and embeds them as myecho, mycat, and so on.
Reference images (64 KiB budget):
| Command | Flat image |
|---|---|
echo |
384 B |
cat |
1,256 B |
wc |
1,821 B |
head |
2,713 B |
grep |
2,854 B |
Concepts you need¶
- Zombies and reaping — exec as a System Call §1, §2
forkreturns twice — exec, File Descriptors, and fork §4- The process tree, and rv6's stand-in for
init— exec as a System Call §3 - Why fork/exec and not
spawn— exec as a System Call §5 execswaps the address space; the shell is just a program — exec as a System Call §6, §7 · rv6 Architecture § Two shells- One seam, two backends: an unedited command becomes
ecalls — ulib and Commands § The rv6 backend · § oslings ship
Read before class¶
| What | Time |
|---|---|
| exec as a System Call §1–§2 (zombies, reaping) | 8 min |
| exec, File Descriptors, and fork §4–§5 | 8 min |
| exec as a System Call §4–§7 (scheduler; the swap; the window; the shell as a program) | 20 min |
| ulib and Commands: The rv6 backend, oslings ship, The budget | 7 min |
| rv6 Architecture: Two shells | 2 min |
Mental model¶
Redirection, ls > out.txt, is not a feature of exec; it is four ordinary calls in the window between fork and exec:
pid = fork() slot 1: sh, Running slot 2: copy of sh, a0 = 0
child: close(1) fd 1 free in slot 2 only
open("out.txt", O_CREATE|O_WRONLY) lowest free fd -> 1
exec("ls", argv) same pid and fds; new page table, epc, sp
...ls writes fd 1, never knowing; exit(0) -> Zombie
parent: wait(&status) yields until slot 2 is a Zombie, then reaps it
fork copies the descriptor table and exec never touches it; that is the case against a single spawn.
Check yourself¶
- Two processes return from the same
forkwith identical memory. How does each know which it is?Answer
Only the return register differs: the child's saveda0is 0, the parent's is the child's pid, and zero is never a valid pid. - Your
echosource has no#[cfg], yet it runs on your laptop and on rv6. Where is the switch, and why isprintln!still forbidden?Answer
Insideulib: the backend is chosen bytarget_os, so on the kernel every call is oneecallwith the number ina7.println!drags incore::fmt, roughly quintupling the largest image.
What "done" looks like¶
oslings run is green, then oslings submit before you leave. Not green? Submit anyway (substantial credit), then finish by Monday 11:59 pm and submit again.
Extra credit today¶
54k_elf_loader (+1.0). Every program so far is a flat image: byte 0 is the entry point, every page read-execute, no .bss. This exercise teaches the kernel to read the ELF the compiler already emits, taking entry point, segment permissions, and a zeroed .bss from its PT_LOAD headers.
If you finish early¶
Work the lecture's Practice Problems, then read xv6 book chapter 1 and the sleep/wakeup section of chapter 7, which rv6's polling wait omits. Tuesday Dec 8 is the payoff lecture and final review; the final is Dec 11–17.