I/O
Follow a `read()` from the call to the SSD and back; blocking, non-blocking, asynchronous and multiplexed I/O; and why a file and a socket are the same kind of thing.
One `read(fd, buf, 4096)` goes from a libc wrapper through a mode switch into the VFS, the file system’s block map and the page cache; a hit is a microsecond memory copy, a miss is a block-layer request, a DMA transfer and a wake-up ~100 µs later on an SSD.
Four I/O models differ in two independent questions — does the call return before the data is ready, and who moves the data — and high-concurrency servers exist in the corner where the kernel tells you readiness (epoll/kqueue) or completion (io_uring/IOCP) so one thread can wait on thousands of descriptors.
A thread per socket is too expensive and a polling loop burns CPU, so the kernel provides one call that sleeps on many descriptors and returns the ready ones — `select` and `poll` scan the whole set per call, `epoll` and `kqueue` keep an interest list and return only what changed, and Windows IOCP reports completions instead of readiness.
On Unix-style systems a file, a socket, a pipe, a device, an eventfd and a timer all sit behind descriptors that answer to the same `read`/`write`/`close`/`poll` verbs; what differs is whether the thing is seekable, whether reads and writes can be short, and what end-of-file means.