user/zombie.c
About this file
zombie deliberately creates a zombie process, so that you can see one. A zombie
is a process that has exited but whose parent has not yet called wait. Its open files
and current directory are already released, but its memory, page table and
process-table slot (with state ZOMBIE) stay allocated until a parent collects it with
wait (kwait → freeproc); the slot holds the exit status
the parent may ask for.
The program forks. The child exits at once; the parent sleeps for 5 clock ticks (about
half a second) and then exits without calling wait. During that half second the
child is a zombie. When the parent exits, kexit calls reparent,
which hands the zombie to init; init’s endless wait loop (user/init.c) then
collects it and the slot is freed (kwait → freeproc). That is
what the comment’s “must be reparented at exit” refers to.
How to see it. Type zombie and press Ctrl-P within half a second. Ctrl-P makes the
console interrupt handler call procdump (kernel/console.c:152), which
prints every process’s PID, state and name. You see something like:
1 sleep init
2 sleep sh
23 sleep zombie
24 zombie zombie
Process 23 is the parent, sleeping in pause; process 24 is the exited child. (The PIDs
depend on how many processes ran before.) Press Ctrl-P again after the prompt returns and
both are gone. To get more time, change the 5 to a larger number of ticks.
Read before: user/kill.c. Read next: kernel/proc.c (kexit,
kwait, reparent).
Purpose and headers
The comment describes the program accurately. user/user.h declares fork,
pause and exit; kernel/stat.h is not used.
main(): a child that exits before its parent waits
fork (sys_fork → kfork) creates a copy of this process. It
returns the child’s PID (process ID) (a positive number) in the parent and 0 in the child.
- The child sees 0, skips the
if, and callsexit(0)on line 13. Inkexitit closes its files, stores its status inxstate, and sets its state toZOMBIE. It wakes its parent in case the parent is inwait, but this parent is not, so nothing collects it. - The parent calls
pause(5)(sys_pause), which sleeps until the tick counter has advanced by 5. Ticks come from the timer interrupt on hart 0, about 10 per second (clockintr), so this is about half a second. Then the parent reaches the sameexit(0).
When the parent exits, reparent gives the zombie child to init and wakes
init, whose wait frees the child. Meanwhile the shell, which was waiting for the
parent only, prints its prompt.
The comment “Let child exit before parent” states an expectation, not a guarantee:
nothing forces the child to run within those 5 ticks. In practice, with 3 CPUs, it
exits almost immediately. If it did not, the program would still be correct: the
child would then exit as an orphan already owned by init, and simply not be a
zombie for long.
main is declared (void) here because the program takes no arguments; the
library’s start passes argc and argv anyway, which is harmless on
RISC-V because they are passed in registers that main ignores.
Fork. Only the parent, which gets the child’s positive PID, enters the if. (If
fork fails it returns -1, and the process exits without creating a zombie.)
Parent: sleep for 5 ticks (about 0.5 s) while the child exits and becomes a zombie.
pause is xv6’s name for the call that other versions call sleep.
Both processes end here: the child immediately, the parent after its pause.