Skip to content

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: ~35 min · Lecture: Week 15 · fork, wait, and the User Shell

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 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

Read before class

What Time
Week 15 · This week, then Friday · 51k fork, exit, and wait through What the child gets 5 min
Week 15 · exit, the zombie, and wait and The scheduler finally has work 3 min
Week 15 · Friday · 52k exec as a system call, all three sections 4 min
Week 15 · Friday · 53k Ship your commands through What oslings ship does 3 min
Week 15 · The long question, one layer per line, with the 54k extra-credit box 3 min
Week 15 · For the exam. Not needed today; on the Final 3 min
ulib and Commands: The rv6 backend, oslings ship and The budget 7 min
rv6 Architecture: Two shells 1 min

Mental model

Redirection, ls > out.txt, is not a feature of exec; it is two 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|O_TRUNC)   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

  1. Two processes return from the same fork with identical memory. How does each know which it is?
    AnswerOnly the return register differs: the child's saved a0 is 0, the parent's is the child's pid, and zero is never a valid pid.
  2. Your echo source has no #[cfg], yet it runs on your laptop and on rv6. Where is the switch, and why is println! still forbidden?
    AnswerInside ulib: the backend is chosen by target_os, so on the kernel every call is one ecall with the number in a7. println! comes from std, so it does not exist on the no_std kernel target; even a core::fmt stand-in would roughly quintuple the largest image.

What "done" looks like

oslings run is green, then oslings submit before you leave. Not green? Submit anyway — the tests that pass earn their share — then finish it at a make-up session (office hours, on the class network) for three quarters, or anywhere else for half, 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 Week 15 · Practice problems on paper, starting with Problem 1: Predict the output and Problem 4: Size a shipped command, 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 final exam, in class, the last day of the term — there is no exam during finals week. Practice Set 3 goes out today and its solutions go up Monday Dec 7, so attempt it on paper first.