Prep: cat — 11c¶
Session: Thursday Sep 24, 1h45 · Exercises: 11c_cat · Prep time: ~45 min · Lecture: Buffers, Bytes, and Line-Oriented I/O
What you will build¶
A cat that streams. Named files are copied to standard output in order with nothing between them; with no arguments it copies standard input, so it works inside a pipeline. Every byte moves through one fixed buffer via the four calls from lecture §1: open, read, write (always through write_all), and close. A file that cannot be opened gets a message on standard error, the remaining files still print, and the exit status ends up non-zero. The tests feed in-memory files — one larger than the buffer, one missing — and check the bytes on standard output, the cannot open message on standard error, and the exit code.
Concepts you need¶
- File descriptors and the three standard streams — L07 §1 · ulib guide § API surface
- The short-read contract;
0is the only end of file — L07 §2 · ulib guide § API surface - Short writes,
write_all, and what the harness cannot check — L07 §3 · ulib guide § Host backend and test harness - One fixed buffer, no allocator — L07 §4 · ulib guide § Portability rules
openwithO_RDONLY,close, and descriptor leaks — L07 §7 · ulib guide § API surfaceResult,?, and the exit status — L06 §6 · ulib guide §ulib::main!
Read before class¶
| What | Time |
|---|---|
| L07 §§1–4 | 25 min |
L07 §7 (cat paragraph), §8 |
5 min |
| ulib guide § The complete API surface · § Portability rules | 10 min |
| ulib guide § The host backend and the test harness | 5 min |
Mental model¶
A 300-byte file copied through a 128-byte buffer into a pipe with room for only 100 bytes when the first write lands:
read -> 128 write_all(&buf[..128]): write -> 100, write -> 28
read -> 128 write_all(&buf[..128]): write -> 128
read -> 44 write_all(&buf[..44]): write -> 44
read -> 0 stop: 300 bytes out
Three details matter. The loop ends on 0 and nothing else; the 44 is a normal short read. The slice handed to the writer is ..n, because bytes past n are leftovers from the previous round. And write_all absorbed a short write that bare write would have dropped silently. Your Module 2 kernel sits on the far side of these same calls with a 128-byte staging buffer and no allocator; this loop is what its trap path looks like from the inside.
Check yourself¶
readinto a 512-byte buffer returns 300. Is the file finished?Answer
No. Only0means end of file; the 300 might be the file's last bytes, a pipe's contents so far, or a kernel clipping the request. Read again; stop at0.cat missing.txt real.txt— what goes to standard output, to standard error, and what exit status?Answer
real.txt's contents on standard output; acannot openmessage namingmissing.txton standard error; exit status 1. Report, remember, continue: a failure is recorded, not returned from, so later files still print and the status stays non-zero.- Tests are green, but you used bare
writeand never closed anything. What happens in December?Answer
The host harness accepts every write whole and grows its descriptor table, so it sees neither bug. On rv6 a short write silently truncates output, and each unclosed descriptor holds a slot in a small fixed table untilopenstarts failing on good files.
What "done" looks like¶
oslings run is green, then oslings submit before you leave. Not green? Submit anyway (substantial credit), then finish by Thursday 11:59 pm and submit again.
If you finish early¶
Work lecture Problem 1 and Problem 2 (counting calls; four broken copy loops). Then Rustlings error_handling, options, primitive_types; 100 Exercises To Learn Rust chapter 5 (Result and ?) and chapter 6 (arrays and slices). Friday's 12c_wc and 13c_grep build on this loop; L07 §7 previews both.