From read()/write() to disk: the fd→file→inode→address_space chain (and the shared offset), buffered reads/writes through the page cache with readahead & writeback/fsync durability, and the alternatives — O_DIRECT, mmap, O_SYNC, sendfile/splice — plus sharing/locking.
What actually happens between read()/write() and the disk — the descriptor chain, the
page cache in the middle, and the choices (buffered, direct, mmap, sync) that change the path. Original,
interview-focused notes; builds on the VFS and
the page cache.
A file descriptor is a per-process index → afileobject (offset, flags) → a dentry/inode → anaddress_space(the file's page cache). A normal read/write flows through the page cache; the interesting variations are the ones that don't.
fd (int) → fd table → struct file {offset, flags} → dentry → inode → address_space
(shared by dup/fork) (the file's cached pages)
file, not the fd — so dup'd fds and fds
inherited across fork share one offset; independently-opened fds don't.pread/pwrite pass an explicit offset and don't touch the shared position — the safe
choice for concurrent access to one file.copy_to_user, no I/O;
miss → allocate a page, read from disk, cache it, copy. Readahead prefetches on
sequential access.copy_from_user into cache pages, mark them dirty, return —
the data is only in RAM. Writeback flushes it later (dirty thresholds / flusher threads).fsync/fdatasync forces the file's dirty pages
(and, for fsync, metadata) to stable storage. "The write succeeded" ≠ "the data is on disk."| Method | Path | Use |
|---|---|---|
| Buffered | Through the page cache | Default; benefits from caching & readahead. |
Direct (O_DIRECT) | Bypasses the page cache, DMA to/from user buffers (aligned) | Databases with their own cache; avoids double-buffering. |
| mmap | Maps page-cache pages into the address space | File access = memory access; shared read-only text/libs; random access. |
Synchronous (O_SYNC) | Each write waits for stable storage | When you can't call fsync yourself; slower. |
sendfile/splice | Kernel-to-kernel, no user copy | Zero-copy file→socket (web/file servers). |
O_DIRECT reader on the same file can see stale/incoherent
data — mixing the two is a known footgun.flock (whole-file, advisory) and fcntl byte-range locks
coordinate writers — advisory, so only cooperating processes honor them.read()/write() through the fd → file → inode → page-cache chain.file; dup/fork share it — use pread/pwrite)write() returns? What guarantees durability? (no; fsync)