A buffered file read can cause Linux to fetch more data than the application explicitly requested. The extra I/O is readahead: the kernel populates nearby page-cache folios in anticipation of continued access.

This behavior sits between application read size and storage request size. A process may issue modest read() calls while the kernel submits larger reads to keep later accesses from waiting on storage.

Readahead is page-cache speculation

Buffered file I/O normally passes through the page cache. When requested file data is absent, the kernel must arrange I/O for that miss. The readahead path can extend that operation across additional folios that are not yet present in the cache.

The speculative region is not simply a fixed number of bytes appended to every read. Linux tracks readahead state for an open file and adjusts the window from observed access behavior.

A useful separation is:

application request
        |
        v
page-cache lookup
        |
        +-- hit --> return cached data
        |
        +-- miss --> required read + possible readahead

Readahead changes which file regions enter the cache and when storage I/O is submitted. It does not change the byte range returned by the application-facing read operation.

Sequential access can grow the window

Linux maintains readahead state in struct file_ra_state. The state includes the current window size and an asynchronous portion used to continue a stream before the application reaches its end.

Once sequential access is established, later readahead requests can be sized from the preceding window. The kernel can scale that size upward, subject to its limits and access-state decisions. This allows a stream that continues predictably to move from demand-driven misses toward asynchronous prefetching.

The resulting pattern can resemble:

access:    [A]
I/O:       [A B]

access:      [B]
I/O:           [C D E F]

access:          [C D]
I/O:                     [G H I J ...]

The diagram is conceptual rather than a statement of exact window sizes. Actual sizing depends on kernel logic, request size, cached regions, and the readahead state associated with the file.

The asynchronous tail carries the next trigger

A readahead request has a total region and an asynchronous tail. The kernel marks a folio in that tail so that accessing it can trigger another readahead operation.

This creates a pipeline. The application consumes data already fetched into the page cache while the kernel starts I/O for a later region. With a sufficiently steady stream and storage completing requests in time, the synchronous component can disappear from subsequent windows.

The mechanism also avoids scheduling a new speculative operation on every cached folio. A marked folio acts as the point at which the current window has been consumed far enough to justify extending it.

Existing cache contents constrain the region

Readahead targets folios that are absent from the page cache. A folio already present does not need another speculative read merely because it falls inside a candidate window.

This makes cache topology part of readahead behavior. Interleaved readers, previous reads, memory-mapped access, and other activity can leave populated regions around the current offset. The kernel searches for an unpopulated position when determining where a new region starts.

A present but non-current folio is a separate case. Readahead does not repair it through the speculative path; the normal single-folio read path can handle that condition.

The distinction prevents readahead from being treated as a bulk overwrite of page-cache state. It is primarily an attempt to fill future gaps.

A cache miss and a marked folio are different triggers

The synchronous readahead path is associated with a cache miss. The required data has not arrived, so the read may have to wait while the kernel submits the necessary I/O.

The asynchronous path is triggered when access reaches a folio carrying the readahead marker. At that point the current data is already available, and the event signals that another region should be pulled toward the cache.

These triggers represent different positions in the I/O pipeline:

cache miss
  -> current demand needs I/O
  -> synchronous readahead path may include extra folios

marked cached folio
  -> current demand is already satisfied
  -> asynchronous readahead can extend the stream

The second case is the mechanism that lets a stable sequential stream run ahead of application demand.

Filesystems still control submission details

The page-cache readahead code prepares the region, but filesystem code participates in turning that region into actual reads. Filesystems can implement the readahead address-space operation, and iomap-based filesystems can use iomap_readahead.

A filesystem may fail to initiate I/O for some speculative folios. The required portion is more important than the asynchronous tail: speculative work can be abandoned when resources or storage conditions make it unattractive.

If a required folio remains unread, the buffered-read path can fall back to the filesystem’s single-folio read operation. Readahead therefore improves the timing and grouping of reads without making successful speculative submission a correctness requirement.

Large storage reads do not prove large application reads

Block-level traces can show requests substantially larger than the application’s explicit read size. Readahead is one source of that mismatch.

For example, an application repeatedly reading 16 KiB from a regular file can establish sequential behavior while the kernel fetches a wider future range. A storage trace then describes kernel I/O policy as well as application demand.

This matters when interpreting latency and throughput data. Bytes fetched speculatively can improve later read latency, but they also consume device bandwidth and page-cache capacity. If the access stream stops or changes direction, some prefetched data may never be consumed.

Application request size, page-cache activity, and block-device request size are therefore distinct measurements.

Random access limits useful speculation

Readahead depends on evidence that nearby future data is likely to be used. A sequence of unrelated offsets provides weak evidence for a growing window.

The kernel also has to cope with patterns that only partially resemble a sequential stream. Multiple readers can interleave offsets, cached pages can interrupt a region, and short-lived sequential runs can end before prefetched data is touched.

Consequently, readahead is not a guarantee that every sequential-looking request produces a particular storage request shape. It is adaptive speculation constrained by cache state and kernel policy.

Memory pressure can erase the benefit

Prefetched folios occupy page-cache memory and remain subject to reclaim. If memory pressure removes them before the application reaches them, the speculative I/O did not eliminate the later miss.

That failure mode can produce extra work: data enters memory early, is reclaimed, and may then need to be read again when demand finally arrives. Workloads with large streaming scans competing against a valuable working set can expose this cost.

The readahead mechanism therefore trades additional near-term I/O and memory occupancy for a chance to hide future storage latency. Its value depends on temporal distance: data must arrive early enough to avoid a stall, but not so early that it consumes resources long before use.

Readahead changes timing, not file semantics

Readahead does not expose bytes beyond the application’s requested range, alter file offsets, or make speculative data durable. It changes the timing of buffered reads into the page cache.

That boundary is central to its design. A speculative request may be reduced, skipped, or lose its cached pages later without changing the correctness of a normal file read. Demand I/O remains the fallback.

For sustained sequential access, this separation lets Linux overlap storage work with application processing. For irregular access, the same separation allows speculation to recede without requiring applications to coordinate each page-cache fill themselves.