Linux Page Cache and Dirty-Page Writeback
Linux’s page cache is the kernel’s in-memory collection of recently used file contents. A file is divided into fixed-size blocks called pages, and Linux keeps some of those pages in RAM so future reads can avoid the storage device.
The cache also provides the normal staging area for file writes. A process can modify a cached page quickly; Linux can then write that page to storage later. That separation is the purpose of the mechanism: storage is slow and has limited concurrency, while RAM is fast and lets the kernel combine, reorder, and delay I/O.
The problem the cache solves
Without a page cache, each ordinary file read would have to wait for the storage device, even if another process had read the same bytes moments earlier. Repeated reads would perform repeated device operations, and small reads would be especially inefficient because storage prefers larger, queued requests.
Writes have a similar problem. Requiring every write() call to wait until the device has completed would make application latency depend directly on storage latency. It would also prevent the kernel from combining adjacent small writes into larger operations.
The page cache turns both operations into memory work when possible, while the kernel handles storage traffic in the background.
What is actually cached
Linux identifies cached file data by the file and its offset. The cache is not simply a copy of an entire named file: it contains individual pages that may be present, absent, clean, or modified. A clean page agrees with the current contents on storage. A dirty page has newer contents in RAM that have not yet been written back.
The cache occupies ordinary physical memory, but it is generally reclaimable. If applications need that RAM, Linux can discard clean cached pages; the next access will fetch them again. Dirty pages cannot be discarded until their contents have been written back or otherwise preserved.
This is why a machine can appear to have little “free” memory while still having plenty available for applications. Tools such as /proc/meminfo expose related counters, including Cached, Dirty, and Writeback.
How a cached read is served
For a normal buffered read(), Linux first looks for the requested file pages in the page cache.
- If the needed page is already cached, Linux copies the requested bytes from that page into the process’s buffer. No storage request is needed.
- If the page is missing, Linux creates or obtains a page-cache entry and asks the filesystem to read the corresponding file data from storage.
- The process waits for that I/O when it needs the result. Once the device completes, the page contains the file data, and Linux copies the requested range to the process.
- Later reads of that page can use the cached copy.
Linux often performs read-ahead, meaning it requests nearby pages before the application asks for them. Sequential readers therefore pay for one storage request while getting several future reads prepared in memory. Read-ahead is an optimization, not a promise: random access may cause Linux to fetch only the pages actually needed.
The same cache is involved when a file is accessed through mmap(), which maps file contents into a process’s address space. Accessing a missing mapped page causes a page fault, an event that tells the kernel the memory page is not currently available to that process. Linux can then load the file page into the page cache and map it for the process. The application still experiences a delay on the first access, but later accesses can use the resident page.
How writes create dirty pages
With ordinary buffered file output, a write() usually copies data from the process into the relevant page-cache pages. Linux marks those pages dirty. The call may return after that memory operation, before the storage device has received the data.
That behavior makes writes fast, but it changes what success means. A successful write() normally means that the kernel accepted the bytes; it does not necessarily mean that the bytes are on durable storage.
The kernel cannot defer dirty pages forever. Too many of them would consume memory and make a crash lose a large amount of acknowledged data. Linux therefore tracks dirty memory and starts writeback, the process of sending modified cached pages to the filesystem and storage device.
How writeback reaches storage
Background kernel workers periodically find dirty pages, or begin sooner when dirty memory reaches configured limits. Memory pressure can also force dirty pages to be written. If an application creates dirty data faster than storage can handle, Linux can throttle that application so the backlog does not grow without bound.
For each dirty page, the filesystem determines where the file’s data belongs on storage. It prepares a block-I/O request—often called a bio, an internal description of a block-device operation—and submits it to the storage stack. The device may merge it with other requests, queue it, and eventually complete it.
After successful completion, the page is marked clean again. Linux may retain it in the page cache for future reads, but it no longer needs writeback for that version. If the write fails, the kernel records an error; applications may observe that error through later write-related operations or filesystem-specific reporting.
A filesystem may need to write metadata as well as file contents. For example, creating or extending a file can require directory, allocation, or size updates. Journaling filesystems record selected metadata changes in a journal so that filesystem structure can be recovered consistently after a crash. That does not automatically make every acknowledged file write durable.
Writeback is not the same as durability. It means data has been submitted through the storage path. An application that needs a durability boundary normally uses
fsync()orfdatasync()and checks the result. Those calls ask the filesystem to complete the required writes before returning, subject to the guarantees of the filesystem and hardware. A device with unsafe volatile write caching can still affect real-world durability.
Practical consequences
A read that is fast because it hits the page cache may become slow after memory pressure evicts the page. A write that returns quickly may be followed later by writeback stalls, device saturation, or an error. fsync() can therefore expose work that an earlier write() merely queued.
Options such as O_DIRECT can reduce or bypass ordinary page-cache data buffering for particular workloads, but they impose alignment and I/O-management requirements and do not eliminate filesystem metadata or durability concerns. For most applications, the page cache is the normal path: it turns storage into a shared, reusable memory layer, then gradually converts dirty memory into ordered storage operations.