Skip to content
LinuxDeep Dive Published Updated 6 min readViews unavailable

Understanding the Linux Page Cache and How Writeback Actually Works

Understand Linux page-cache reads, buffered writes, dirty throttling and durability boundaries without confusing write completion with stable storage.

Linux uses page cache to connect filesystem I/O and virtual memory. Buffered read(), write(), and file-backed mmap() can operate on the same cached file pages. Clean pages may be reclaimed; dirty pages must be written or discarded according to filesystem semantics before their memory is reusable.

“Used memory is just cache and instantly free” is an unsafe simplification. Reclaim can scan, write back, stall, or fail when pages are dirty, pinned, under writeback, unevictable, or protected by workload policy.

Read MemAvailable, not only MemFree

MemFree is currently unused RAM. Cached, Buffers, reclaimable slab, anonymous memory, shmem, and other categories describe different uses. MemAvailable is a kernel estimate of memory that could support new workloads without swapping, considering reclaimability and watermarks.

free -h
grep -E '^(MemTotal|MemFree|MemAvailable|Cached|Buffers|SReclaimable|Shmem):' /proc/meminfo

It is an estimate, not a reservation. Container views and old kernels/tools can differ, and host totals may not represent a cgroup’s memory.max headroom.

Cache identity is file plus offset

The kernel caches pages associated with an address-space object, generally an inode/file mapping and offset. A second buffered read can hit memory, but readahead, concurrent writes, truncation, direct I/O coherence, filesystem behavior, and memory pressure influence the result.

Benchmarking a “second read” measures more than storage. Document cache state, filesystem, mount, readahead, file size, working set, and whether another process warmed the data.

File-backed mappings use the same coherence domain

With mmap(MAP_SHARED), stores can dirty page-cache pages without a write() syscall. MAP_PRIVATE modifications are private copy-on-write pages and are not normal file writeback. msync() semantics matter when an application needs mapped changes synchronized.

Do not monitor only write syscalls to account for dirty file data. Database and language runtimes may modify mapped files.

Buffered write completion is not durability

A normal successful write() means bytes were accepted according to the file descriptor’s semantics, often copied into cached pages and marked dirty. It can still block on page faults, allocation, locks, dirty throttling, writeback errors, filesystem work, or storage state. Short writes and errors must always be handled.

The data may not survive power loss after write() returns. Applications requiring a commit boundary need the filesystem’s documented synchronization operation and correct ordering.

Dirtying is accounted and balanced

As tasks dirty pages, the kernel compares dirty memory with thresholds and backing-device writeback capability. Background writeback begins before the harder threshold; dirtying tasks can be throttled so production does not outrun storage indefinitely.

The classic sysctls include ratio and absolute-byte alternatives:

sysctl vm.dirty_background_ratio vm.dirty_ratio
sysctl vm.dirty_background_bytes vm.dirty_bytes
sysctl vm.dirty_expire_centisecs vm.dirty_writeback_centisecs

Ratios are based on memory eligible for dirtying, not naively all physical RAM. Byte and ratio forms interact: setting one form can reset its counterpart. Defaults and algorithms vary by kernel; do not paste universal values.

Expiration is not a durability deadline

dirty_expire_centisecs makes sufficiently old dirty data eligible for periodic writeback consideration. It does not promise every byte reaches stable media by that time. Congestion, device failure, filesystem journaling, laptop mode, cgroups, and writeback ordering can delay completion.

Likewise dirty_writeback_centisecs controls periodic flusher wakeups, not a transaction SLA. Durability belongs in the application protocol.

Writeback is per backing device and mapping

Kernel writeback infrastructure associates dirty pages with a backing device/writeback context and lets filesystems submit I/O. On modern systems, worker names and internal implementation change; relying on historical pdflush or one bdi-* thread name is brittle.

Inspect behavior with supported tools and tracepoints, correlating device/filesystem latency:

grep -E '^(Dirty|Writeback|WritebackTmp):' /proc/meminfo
cat /proc/pressure/io

Persistent dirty growth can mean workload exceeds sustainable bandwidth, not necessarily a broken device. Add disk error logs, queue latency, filesystem health, and cgroup I/O stats.

fsync() defines a file durability request

fsync(fd) flushes changed file data and metadata needed to retrieve it, subject to filesystem/device guarantees and error reporting. fdatasync() may omit metadata not necessary for later data access. Applications must check the return value; delayed writeback errors can surface at synchronization or close-related paths.

For create/rename durability, syncing the file alone may not persist the directory entry. A robust replace protocol typically writes a temporary file, checks writes, synchronizes it, renames atomically within the filesystem, then synchronizes the containing directory according to filesystem/application requirements.

Test crash behavior on the target filesystem; POSIX API names do not erase storage-stack differences.

O_SYNC, O_DSYNC, and direct I/O are distinct

O_SYNC/O_DSYNC change write-completion synchronization semantics. O_DIRECT attempts to minimize cache effects and imposes alignment/filesystem constraints, but it is not a durability guarantee. Direct I/O may still interact with device caches and concurrent buffered/mapped I/O; mixing paths requires documented coherence rules.

Do not recommend O_DIRECT as “opt into synchronous durability.” Databases use it as part of a carefully designed cache and flush protocol, not as a substitute for one.

Device caches remain below the filesystem

The block layer and device may report completion while volatile caches retain data unless flush/FUA semantics are correctly implemented. Filesystem, kernel, transport, virtualization, RAID controller, and physical device must all honor the contract.

Battery-backed/power-loss-protected hardware changes the risk; a consumer drive that lies about flushes breaks assumptions. Validate with vendor documentation, power-failure testing where justified, and storage monitoring—not timing alone.

Global sync is a blunt diagnostic

sync schedules/requests writeback broadly and can block behind unrelated filesystems or devices. A slow sync does not identify which file or application dirtied data. Avoid running repeated global syncs on a busy production host as a “fix”; they perturb the system and hide causal timing.

Prefer application/file-specific synchronization, filesystem-specific observability, and controlled tests. Unmount requires flushing and can expose latent errors, but forced/lazy unmount changes guarantees and is not a routine performance tool.

Dropping caches is not optimization

The drop_caches sysctl can release clean page cache and reclaimable slab for testing after appropriate synchronization, but the kernel documentation explicitly does not recommend it as a way to control normal cache growth. Re-reading data creates I/O/CPU cost and can cause latency spikes.

Never schedule echo 3 > /proc/sys/vm/drop_caches as maintenance. For repeatable benchmarks, use an isolated machine/dataset and disclose the cache preparation method.

Memory cgroups include page cache

cgroup v2 accounts file memory as well as anonymous and kernel categories. A container can hit memory.max because of charged page cache even when the host has available RAM. Reclaim and writeback can occur within the cgroup; memory.stat, memory.events, and pressure files provide evidence.

cat /sys/fs/cgroup/your.scope/memory.stat
cat /sys/fs/cgroup/your.scope/memory.events

Use the actual manager-provided path. Do not assume host free explains a container OOM.

Build a controlled writeback experiment

On disposable storage, record kernel, filesystem/mount, device stack, cache protection, cgroup, dirty sysctls, and baseline. Generate bounded writes, sample Dirty/Writeback, PSI, device latency and throughput, then call and check the intended file synchronization operation. Verify content hashes after clean remount and, only if risk justifies it, a controlled power-failure test platform.

The design is correct when application acknowledgement matches its documented durability level, throttling occurs before uncontrolled accumulation, errors reach the caller/monitoring, memory pressure remains bounded, and recovery reproduces the last confirmed commit—not merely the last successful write().

Related:

Sources:

Comments