xv6, line by line
tour 33
Tours33 The life of an inode

Tour 33 · File system · about 27 minutes · 16 steps

The life of an inode

You type cat README &; rm README. Two programs start at once: cat opens README and starts printing it, and on another hart rm deletes it. And yet cat prints the whole file, to the last line. Only when cat closes it does the file’s space go back to the disk.

This tour follows one inode, inode 2 (README, 2441 bytes in blocks 48, 49 and 50), through its whole life in memory: iget finds it a slot in the inode table, ilock reads it from disk, the open file holds a reference while cat reads, rm takes away its last name, and the final iput truncates it and frees it. On the way you will see two counts that are easy to confuse: ref, how many pointers in memory refer to the inode, and nlink, how many directory entries name it. A file dies only when both reach zero.

Two of the interleavings here are real races: two harts looking up the same inode at the same moment, and a bug in iput fixed in this very version of xv6 (commit d7e85f1). Both are shown as timelines.

Best after: 17. Sleep-locks, 30. The buffer cache, 31. The log: begin_op, commit and group commit

Who is running where

The machine has three harts. The shell turned the command line into three processes (on a fresh boot): rm is pid 3, an intermediate shell is pid 4, and cat is pid 5. When the tour starts:

Hart What it is doing
0 Idle in its scheduler
1 Running cat (pid 5): the process this tour follows
2 Running rm (pid 3), which will delete README while cat reads it

We traced a copy of the kernel running exactly this command, so the slot numbers and transactions below are real.

Three harts are running. This tour follows one path through the code, but the machine has three CPUs executing at the same time. Watch the locks held display at the top of each step, and read the Meanwhile, on other harts boxes: they show what the other CPUs could be doing at that very moment.
The route
  1. 1cat asks for README by name user/cat.c
  2. 2dirlookup finds the entry and asks iget for the inode kernel/fs.c
  3. 3iget gives inode 2 a slot, under itable.lock kernel/fs.c
  4. 4Two harts, one inode number kernel/fs.c
  5. 5Back in namex, the directory is let go kernel/fs.c
  6. 6ilock locks the inode, and reads it the first time kernel/fs.c
  7. 7The open file keeps the reference, not the lock kernel/sysfile.c
  8. 8Each read locks it again, briefly kernel/file.c
  9. 9Meanwhile rm takes away the last name kernel/sysfile.c
  10. 10cat keeps reading a file with no name kernel/fs.c
  11. 11close drops the file, and the file drops the inode kernel/file.c
  12. 12iput sees the last reference to a nameless inode kernel/fs.c
  13. 13Truncate, with only the sleep-lock held kernel/fs.c
  14. 14Drop the reference, then free the number kernel/fs.c
  15. 15The race this order prevents kernel/fs.c
  16. 16An inode's whole life, in four states kernel/fs.c

Keys: ← → step · Home start