In this lesson, you will learn how to bypass the kernel’s page cache to exert fine-grained control over data movement between your application and persistent storage.
The Buffered I/O Default
By default, Linux employs Buffered I/O. When your application issues a read() or write() system call, the kernel intercepts the data, copying it into or out of the Page Cache (the kernel's pool of physical memory pages used to cache disk blocks).
This is highly efficient for general workloads: frequent reads benefit from RAM-speed access, and writes are "lazy," allowing the kernel to coalesce updates and flush them to disk asynchronously, which helps mitigate disk latency.
Why Bypass the Cache? (Direct I/O)
However, for advanced systems like high-performance databases (e.g., MySQL or PostgreSQL), the Page Cache can be a liability. If the database already manages its own memory buffer pool, having the kernel cache the same data is redundant—wasting precious RAM and causing "double buffering." Furthermore, the kernel's background writeback processes (which you learned about in our unit on dirty page writeback) can introduce unpredictable latency spikes during heavy I/O.
To circumvent this, you use Direct I/O (often via the O_DIRECT flag in open() calls). This allows the application to perform I/O directly between the user-space buffer and the storage device, completely bypassing the kernel page cache.
Implementation Concept
To use O_DIRECT successfully, your application must align its buffers and file offsets with the underlying hardware’s block size (typically 512 bytes or 4KB):
// Bypassing the kernel cache for a raw data stream
int fd = open("database.dat", O_RDWR | O_DIRECT | O_SYNC);
// The kernel will now transfer data directly from this buffer to the disk
read(fd, aligned_user_buffer, buffer_size);
While Direct I/O eliminates the overhead of memory copying and provides deterministic performance, it removes the kernel's ability to optimize I/O patterns. You are now responsible for the performance of the access pattern, as the kernel will no longer readahead or cache your blocks.
Key Takeaway: Use Buffered I/O for general-purpose application flexibility; switch to Direct I/O when your application manages its own caching and requires predictable, high-throughput storage performance without kernel-level interference.