A Linux timerfd becomes readable when its configured timer has expired. The bytes returned by read(2) are not a timestamp or event record: they encode one unsigned 64-bit integer containing the number of expirations since the previous successful read. Timer state therefore participates in the same readiness machinery as sockets and pipes while retaining timer-specific semantics behind the descriptor boundary.
Readiness represents a pending expiration count
timerfd_create(2) creates a descriptor associated with a clock, while timerfd_settime(2) arms or disarms its timer. Once at least one expiration is pending, poll(2), select(2), and epoll(7) can report the descriptor as readable.
A successful read(2) requires a buffer large enough for an unsigned 64-bit value. It returns the accumulated expiration count and consumes that pending count. A periodic timer serviced late can therefore return a value greater than one. Treating every readable notification as exactly one elapsed interval discards information preserved by the kernel.
This distinction matters in event loops. Readiness states that a count can be consumed; it does not state that exactly one deadline just occurred. If processing stalls for five periods, a later read can report those five expirations together.
Clock selection defines the time domain
The clock passed to timerfd_create(2) is part of the timer contract. CLOCK_MONOTONIC advances monotonically and is not affected by discontinuous wall-clock changes. CLOCK_REALTIME represents system wall-clock time and can move when that clock is changed. Linux also provides related clock choices with suspend and alarm semantics where supported and permitted.
Without TFD_TIMER_ABSTIME, the initial expiration is relative to the selected clock’s current value. With that flag, the supplied expiration is interpreted as an absolute value in the selected clock’s domain. An absolute CLOCK_REALTIME timer consequently has a deliberate relationship with wall-clock adjustment that a relative CLOCK_MONOTONIC timer does not.
For supported real-time clocks, TFD_TIMER_CANCEL_ON_SET can be combined with an absolute timer. A qualifying discontinuous clock change can then cause read(2) to fail with ECANCELED. This condition depends on clock choice, absolute mode, the cancellation flag, and the clock change; it is not a generic property of monotonic timers.
Periodic timers preserve overruns as a count
An interval in struct itimerspec makes the timer periodic after its initial expiration. Unread expirations accumulate in the counter returned by the descriptor rather than requiring one user-space event object per period.
That separates scheduling cadence from consumer cadence. A late consumer can process each missed logical tick, coalesce work, or advance state directly by the reported count. The descriptor does not impose one of those policies. Several expirations can collapse into one readiness observation while the subsequent read still exposes their multiplicity.
TFD_NONBLOCK changes empty-state I/O: when no expirations are pending, read(2) fails with EAGAIN instead of waiting. TFD_CLOEXEC sets close-on-exec at creation time and affects descriptor inheritance rather than timer behavior. These descriptor flags are separate from the clock and itimerspec that define timer behavior.
Reconfiguration replaces one timer’s state
Calling timerfd_settime(2) reconfigures the timer associated with the descriptor. A zero initial expiration disarms it; a nonzero interval controls periodic expirations after the next initial expiration. The optional old-value argument reports the prior timer setting, but it does not form a transaction with unrelated descriptors or application state.
If several components share one timer descriptor and independently rearm it, the last reconfiguration controls that single kernel timer. The descriptor is one timer object, not a multi-client deadline registry. Application-level ownership must account for that replacement behavior.
Putting timers and sockets into one epoll set also does not create a globally ordered stream across independent kernel sources. Nor does consuming a timer count synchronize memory shared with another thread. timerfd defines timer and descriptor behavior, not language-level memory ordering.
Its boundary is narrower: Linux timer state becomes pollable I/O state, expiration multiplicity survives delayed consumption, and clock semantics remain explicit. Descriptor-oriented systems can integrate deadlines into an existing readiness loop while retaining responsibility for scheduling policy, synchronization, and clock choice.