eventfd
eventfdis the lightest-weight wait/notify channel Linux offers: the kernel maintains a single unsigned 64-bit counter behind one file descriptor, and that counter is the entire object. You create it witheventfd(initval, flags), which returns one fd (not the two a pipe needs); awrite()of an 8-byte integer adds that value to the counter; aread()of 8 bytes returns the counter and resets it to zero (or, underEFD_SEMAPHORE, returns the value1and decrements the counter by one). The decisive property is that the descriptor is pollable — it is reported readable whenever the counter is greater than zero, and writable whenever a further add will not overflow — so it slots directly into aselect()/poll()/epoll()loop alongside sockets, pipes, and timers (eventfd(2)). Because it is so cheap, it is also the kernel’s preferred kernel→userspace doorbell: asynchronous I/O (AIO), KVM’s irqfd, andio_uringcompletion notifications all signal an eventfd to wake a userspace event loop. This note pins the mechanism to the Linux 6.12 LTS source (fs/eventfd.c, released 2024-11-17), authored originally by Davide Libenzi.
eventfd() was added in Linux 2.6.22 (glibc wrapper since glibc 2.8); the eventfd2() variant that accepts a flags argument arrived in Linux 2.6.27, and EFD_SEMAPHORE followed in Linux 2.6.30 (eventfd(2) HISTORY). Since glibc 2.9 the eventfd() library wrapper transparently uses the eventfd2() syscall when the running kernel supports it, which is why the userspace function takes flags even though the original syscall did not.
Mental Model
The right way to think about an eventfd is “a counting semaphore wearing the costume of a file.” There is no buffer of distinct messages — there is one number. Writers do not enqueue data; they bump a count by an arbitrary 64-bit amount. Readers do not dequeue messages; they drain the count (all of it at once, by default) or, in semaphore mode, take exactly one unit per read. The genius of the design is purely about composability: because the count’s “non-zero-ness” is exposed through the standard EPOLLIN readiness machinery, an event loop never special-cases “is this an eventfd?” — it treats the descriptor identically to a socket that has data. The same loop can wait on a socket becoming readable, a timerfd expiring, a child exiting via a pidfd, and “some background thread finished a job” — the last one being exactly what an eventfd expresses.
flowchart LR W["writer<br/>write(fd, &u64, 8)"] -->|"count += u64"| CTX["eventfd_ctx<br/>__u64 count"] KSIG["kernel signaller<br/>eventfd_signal()"] -->|"count += 1"| CTX CTX -->|"wake_up_locked_poll(EPOLLIN)"| WQ["wait queue ctx->wqh"] WQ --> EPOLL["epoll / poll / select<br/>reports readable when count > 0"] EPOLL --> R["read(fd, &u64, 8)<br/>returns count, resets to 0<br/>(or returns 1, count-- under EFD_SEMAPHORE)"] R -.->|"draining frees writers<br/>blocked on overflow"| W
How a notification crosses the eventfd. What it shows: any number of writers (userspace write()s, or in-kernel eventfd_signal() calls) add to the same __u64 count; each add wakes everything sleeping on the descriptor’s wait queue with EPOLLIN. An epoll loop sees the descriptor become readable, read()s it, and gets the accumulated total (resetting to zero), or takes a single unit under EFD_SEMAPHORE. The insight to take: the eventfd carries no payload — only “how many times / by how much was I poked” — and its whole value is that “I was poked” is expressed as the same EPOLLIN readiness every other descriptor uses, so notification folds into one uniform event loop.
Mechanical Walk-through
The object. Every eventfd is a struct eventfd_ctx, defined in fs/eventfd.c:
struct eventfd_ctx {
struct kref kref;
wait_queue_head_t wqh;
__u64 count;
unsigned int flags;
int id;
};It is reference-counted (kref), owns a wait-queue head (wqh) on which readers/writers/pollers sleep, holds the live count, remembers the creation flags, and carries a small integer id (allocated from an IDA, surfaced in /proc/PID/fdinfo for debugging). The comment in the source spells out the contract directly: “Every time that a write(2) is performed on an eventfd, the value of the __u64 being written is added to count and a wakeup is performed on wqh. If EFD_SEMAPHORE flag was not specified, a read(2) will return the count value to userspace, and will reset count to zero.”
Creation. Both syscalls funnel into do_eventfd(count, flags). It first sanity-checks the flag layout with build-time assertions — EFD_CLOEXEC == O_CLOEXEC, EFD_NONBLOCK == O_NONBLOCK, EFD_SEMAPHORE == (1 << 0) — so the user-facing EFD_* constants can be passed straight through as open-file flags. It rejects any flag bit outside EFD_FLAGS_SET (which is O_CLOEXEC | O_NONBLOCK | EFD_SEMAPHORE) with -EINVAL. It allocates and initializes the context, seeds ctx->count with the caller’s initval, stores the flags, and then wraps the context in an anonymous inode file named "[eventfd]" via anon_inode_getfile(), wiring up the eventfd_fops file-operations table (.poll, .read_iter, .write, .release, plus an fdinfo dumper). The fd is created O_RDWR plus whatever shared flags (O_CLOEXEC, O_NONBLOCK) the caller asked for, and FMODE_NOWAIT is set so the file participates in non-blocking I/O. The legacy SYSCALL_DEFINE1(eventfd, ...) simply calls do_eventfd(count, 0); the modern SYSCALL_DEFINE2(eventfd2, count, flags) passes the flags through.
Writing — an atomic add with overflow back-pressure. eventfd_write() requires exactly 8 bytes (-EINVAL otherwise), copies the __u64 ucnt from userspace, and immediately rejects ULLONG_MAX (0xffffffffffffffff) — that value is reserved as a sentinel and is never a legal write. It then takes the wait-queue spinlock and tests ULLONG_MAX - ctx->count > ucnt. If the add would not push the counter to ULLONG_MAX or beyond, the write succeeds: ctx->count += ucnt, and wake_up_locked_poll(&ctx->wqh, EPOLLIN) wakes any blocked reader or poller. If the add would overflow, behavior splits on O_NONBLOCK: a non-blocking fd returns -EAGAIN (res was preset to -EAGAIN), while a blocking fd sleeps in wait_event_interruptible_locked_irq(ctx->wqh, ULLONG_MAX - ctx->count > ucnt) until a reader drains enough of the count to make room. This is genuine back-pressure: a writer can block waiting for a reader, exactly like a full pipe. The effective maximum the counter can ever hold is therefore ULLONG_MAX - 1 = 0xfffffffffffffffe, which the man page states explicitly.
Reading — drain or single-unit. eventfd_read() requires the buffer to be at least 8 bytes. Under the lock, if ctx->count is zero, a non-blocking fd returns -EAGAIN and a blocking fd sleeps in wait_event_interruptible_locked_irq(ctx->wqh, ctx->count) until a writer makes it non-zero. The actual read value is computed by eventfd_ctx_do_read():
*cnt = ((ctx->flags & EFD_SEMAPHORE) && ctx->count) ? 1 : ctx->count;
ctx->count -= *cnt;This single line captures both modes. Without EFD_SEMAPHORE, *cnt = ctx->count and ctx->count -= *cnt leaves it at zero — the read drains the whole accumulated total and returns it. With EFD_SEMAPHORE and a non-zero count, *cnt = 1 and ctx->count -= 1 — the read takes exactly one unit and returns the value 1, leaving the rest for subsequent reads. After the read, if the count dropped, the kernel issues wake_up_locked_poll(&ctx->wqh, EPOLLOUT) to wake any writer that was blocked on overflow (there is now room). The 8-byte value is then copied out to userspace.
Polling — the readiness rules. eventfd_poll() registers the caller on ctx->wqh and computes readiness from a single READ_ONCE(ctx->count) snapshot:
if (count > 0)
events |= EPOLLIN; /* readable: there is something to drain */
if (count == ULLONG_MAX)
events |= EPOLLERR; /* overflow sentinel (only kernel signal path reaches it) */
if (ULLONG_MAX - 1 > count)
events |= EPOLLOUT; /* writable: at least one more add won't overflow */So the descriptor is readable when count > 0 and writable when count < ULLONG_MAX - 1 (i.e. when there is room for at least one further add). The EPOLLERR arm only fires when the count has reached ULLONG_MAX, which userspace write()s can never produce (they top out at ULLONG_MAX - 1); it exists for the in-kernel eventfd_signal() path described next. The source carries an unusually long comment proving the lock-free READ_ONCE of count in poll cannot miss a wakeup: poll_wait() takes ctx->wqh.lock when adding the waiter, which acts as an acquire barrier, so the read of count is ordered against the writer’s locked increment — the dangerous interleaving (read stale zero, then writer increments-but-sees-no-waiter) is impossible.
The kernel signalling path. In-kernel producers (AIO, KVM, etc.) do not write(); they call eventfd_signal(ctx), a thin wrapper over eventfd_signal_mask(ctx, 0). This increments by one (if (ctx->count < ULLONG_MAX) ctx->count++) and wakes with EPOLLIN | mask. Critically it is callable from contexts that cannot sleep, and it guards against re-entrancy: it checks current->in_eventfd and WARN_ON_ONCEs if it is already inside an eventfd wakeup, because recursing through waitqueue wakeup handlers could deadlock or overflow the kernel stack. A caller that uses nested waitqueues with custom wakeup handlers is expected to check eventfd_signal_allowed() first and defer the signal to a safe context if it returns false. This is the path that lets the kernel allow count to reach ULLONG_MAX and signal overflow as EPOLLERR, since the kernel side cannot return -EAGAIN to itself.
Configuration and Code
A complete parent/child example showing both the accumulate-and-drain default and the structure of an event loop. Comments are line-by-line on the load-bearing calls.
#define _GNU_SOURCE
#include <sys/eventfd.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <stdint.h> /* uint64_t */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
/* EFD_CLOEXEC: don't leak the fd across an exec().
* EFD_NONBLOCK: read()/write() return EAGAIN instead of blocking,
* which is mandatory if this fd lives in an epoll loop. */
int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (efd == -1) { perror("eventfd"); exit(1); }
if (fork() == 0) { /* child: a "worker" that pokes the parent */
for (int i = 1; i <= 3; i++) {
uint64_t add = i; /* MUST be exactly 8 bytes */
/* write() ADDS to the counter; three writes leave count = 1+2+3 = 6 */
if (write(efd, &add, sizeof(add)) != sizeof(add)) perror("write");
}
_exit(0);
}
/* parent: wait for the eventfd to become readable, then drain it */
int ep = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event ev = { .events = EPOLLIN, .data.fd = efd };
epoll_ctl(ep, EPOLL_CTL_ADD, efd, &ev);
struct epoll_event out;
epoll_wait(ep, &out, 1, -1); /* blocks until count > 0 */
uint64_t total;
/* read() returns the WHOLE accumulated count and resets it to 0.
* Note: the three child writes may have coalesced into one count of 6,
* so a single read can return 6 even though there were three writes. */
if (read(efd, &total, sizeof(total)) == sizeof(total))
printf("drained %llu\n", (unsigned long long)total); /* prints 6 */
close(efd);
close(ep);
return 0;
}The single most common bug this example avoids: the buffer must be exactly 8 bytes. Passing a char buf[1] or an int (4 bytes) to read()/write() yields -EINVAL, because eventfd_read/eventfd_write reject any size other than sizeof(__u64).
The semaphore mode changes the read contract entirely:
int efd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
uint64_t three = 3;
write(efd, &three, sizeof(three)); /* count = 3 */
uint64_t got;
/* Under EFD_SEMAPHORE each read takes ONE unit: */
read(efd, &got, sizeof(got)); /* got = 1, count now 2 */
read(efd, &got, sizeof(got)); /* got = 1, count now 1 */
read(efd, &got, sizeof(got)); /* got = 1, count now 0 */
read(efd, &got, sizeof(got)); /* -1, errno = EAGAIN (count is 0) */This is exactly a counting semaphore: write(N) performs N “ups” (releases), each read() is one “down” (acquire), and a blocking (non-EFD_NONBLOCK) reader sleeps until a unit is available. Used this way, an eventfd is a kernel-backed semaphore that also happens to be pollable — a property no POSIX semaphore has.
Failure Modes and Common Misunderstandings
“My read() returned EINVAL.” The buffer was not 8 bytes. There is no partial read; the value is always a single __u64.
“Writes seem to get lost.” They do not get lost — they coalesce. Three writes of 1 each, before any read, leave a single count of 3. A reader draining once sees 3, not three separate events. If you need to count discrete events one-by-one (e.g. “process exactly one queued job per readiness”), use EFD_SEMAPHORE so each event is one unit consumed by one read; otherwise design for the fact that a default-mode eventfd tells you “at least one thing happened, and here is the summed magnitude”, not “here is event #1, here is event #2.”
“My event loop spins at 100% CPU.” Almost always the eventfd was created blocking (no EFD_NONBLOCK) or the loop reads in level-triggered epoll without draining the count to zero, so the descriptor stays readable and epoll_wait returns immediately every iteration. In an epoll loop the fd should be EFD_NONBLOCK and each readiness event should read() until EAGAIN (or, in default mode, once — which drains fully). With edge-triggered epoll (EPOLLET) draining to EAGAIN is mandatory, or you can miss subsequent notifications entirely.
“A blocking write hung forever.” In default mode a blocking write() that would overflow sleeps until a reader drains the count. If nothing ever reads, the writer blocks indefinitely. This is intended back-pressure, but it surprises people who assumed write() to an eventfd always returns immediately. Use EFD_NONBLOCK to get -EAGAIN instead, or ensure a reader exists. In practice overflow is astronomically unlikely (you would need ~1.8×10¹⁹ unmatched writes of 1), so this matters mainly when writing large arbitrary values.
“EPOLLERR / overflow.” Userspace can never drive the counter to the ULLONG_MAX sentinel — write() caps at ULLONG_MAX - 1 and rejects ULLONG_MAX outright. The EPOLLERR/POLLERR readiness only appears via the in-kernel eventfd_signal() path, which is allowed to reach ULLONG_MAX. For ordinary userspace-to-userspace use you will not see it.
“I closed one end and expected a SIGPIPE.” An eventfd is not a pipe; there is no SIGPIPE and no “broken pipe.” On the last close() the context is freed (after the kref drops). A poller blocked on a closing fd is woken with EPOLLHUP (eventfd_release calls wake_up_poll(&ctx->wqh, EPOLLHUP)).
Alternatives and When to Choose Them
The natural comparison is a self-pipe — the historical idiom of pipe()-ing to yourself and writing one byte to wake a select() loop (Daniel J. Bernstein’s “self-pipe trick”). An eventfd does the same job with one descriptor instead of two, with a much smaller kernel footprint (no ring buffer, just a counter), and the man page recommends it explicitly: “Applications can use an eventfd file descriptor instead of a pipe in all cases where a pipe is used simply to signal events” (eventfd(2)). The pipe wins only if you actually need to convey bytes of payload; the eventfd conveys only a count.
Against a signalfd, the distinction is what you are waiting for: signalfd folds signals (SIGTERM, SIGCHLD) into a pollable fd, whereas eventfd folds arbitrary application-defined pokes into one. You typically use both in the same loop — signalfd for OS signals, eventfd for “wake up, I queued work for you.” Against a timerfd, the timer fires on a schedule the kernel owns; an eventfd fires whenever something (user code or kernel) decides to poke it. Against a POSIX semaphore (sem_post/sem_wait), an EFD_SEMAPHORE eventfd gives the same counting-semaphore semantics but is pollable — a POSIX unnamed semaphore cannot be put in an epoll set, which is precisely why a thread-to-event-loop wakeup uses an eventfd rather than sem_post.
The other major reason to reach for eventfd specifically is kernel→userspace notification, where it is not optional but the designated mechanism: the asynchronous I/O interface, KVM’s irqfd, and io_uring (via IORING_REGISTER_EVENTFD) all require an eventfd as the object the kernel signals to wake your loop, because the kernel can call eventfd_signal() from non-sleeping contexts.
Production Notes
io_uring completion notification. A common high-performance pattern registers an eventfd with an io_uring instance so the ring posts a wakeup when completions arrive, letting a single epoll loop wait on the ring and on regular sockets uniformly. The eventfd is signalled by the kernel via eventfd_signal(), which is why a read() of the completion eventfd returns the number of completion events posted — the same accumulate-and-drain semantics this note describes. This is preferable to busy-polling the completion queue when the application is not otherwise CPU-bound. (Cross-reference the io_uring submission/completion machinery.)
KVM and vhost. The irqfd and ioeventfd note traces how KVM binds eventfds to guest interrupt injection (irqfd) and guest MMIO doorbells (ioeventfd); both are eventfds in exactly the form described here — a vhost kernel thread calls eventfd_signal() on a “call” eventfd to assert a guest interrupt with no userspace round-trip. The eventfd is the shared rendezvous object that lets a third party (the in-kernel backend) ring the doorbell, taking QEMU out of the datapath. That this works at all depends on the in-kernel eventfd_signal() path described above being callable from atomic context.
Thread-to-loop wakeup. In multithreaded servers the canonical use is a worker thread that finishes a job and pokes the I/O thread’s epoll loop: write(efd, &one, 8). The I/O thread, blocked in epoll_wait, wakes, reads the eventfd (draining the count, which conveniently equals “how many jobs finished while I was asleep”), and processes the results. This replaces fragile self-pipe code and avoids the async-signal-safety minefield of using a signal for the same purpose (see Async-Signal-Safety and Reentrant Handlers).
/proc/PID/fdinfo debugging. When CONFIG_PROC_FS is enabled, each eventfd’s fdinfo exposes eventfd-count (current counter, hex), eventfd-id, and eventfd-semaphore (0/1), so you can inspect a stuck counter from outside the process — invaluable when diagnosing a hung writer or a leaked notification.
See Also
- signalfd — sibling fd-event primitive; folds signals into a pollable fd rather than an application counter
- timerfd — sibling fd-event primitive; the same readable-fd pattern for timer expirations
- The epoll Readiness Model and IPC — the readiness model that makes eventfd’s pollability useful
- irqfd and ioeventfd — KVM’s two eventfd doorbells for guest interrupt injection and MMIO kicks
- Async-Signal-Safety and Reentrant Handlers — why an eventfd wakeup is safer than a signal for thread-to-loop notification
- Linux IPC MOC — parent map; §8 File-Descriptor Event Primitives