eventfd(2)
๐ธ eventfd(2) โ the Linux Programmer's Manual, but make it make sense
okay bestie, buckle up, because this one is genuinely iconic. it's a system call in the System Calls Manual for Linux, and it's the moment you stop piping stuff around like it's 1995. ๐
โจ NAME โจ
eventfd โ create a file descriptor for event notification
๐ LIBRARY โจ
Standard C library (libc, -lc)
๐ซถ SYNOPSIS โจ
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
๐ DESCRIPTION ๐
okay so. eventfd() creates something called an "eventfd object", and it's basically the glow-up of event wait/notify. user-space applications use it, and the kernel uses it to ping user space when stuff happens. it's giving โจcommunicationโจ.
inside this object lives an unsigned 64-bit integer (uint64_t) counter, and the kernel is the one maintaining it. yes, the kernel. the counter starts out with whatever you passed as the initval argument. ๐ฑ
and as its return value, eventfd() hands you a brand-new file descriptor that refers to the eventfd object. that fd is your whole personality now.
the following values may be bitwise ORed in flags to change how eventfd() behaves โ like toppings, but for syscalls ๐:
EFD_CLOEXEC(since Linux 2.6.27) โ sets the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. like, if you wanna know why that matters, go read about theO_CLOEXECflag inopen(2), it's all explained there. no cap, it's important.EFD_NONBLOCK(since Linux 2.6.27) โ sets theO_NONBLOCKfile status flag on the open file description (seeopen(2)) that the new file descriptor points at. using this saves you from making extrafcntl(2)calls just to get the same result. efficiency, periodt. ๐ฅEFD_SEMAPHORE(since Linux 2.6.30) โ gives you semaphore-like semantics for reads from the new file descriptor. more on that below ๐
โ ๏ธ if you're on Linux up to 2.6.26, the flags argument is completely unused and you must specify it as zero. no options, no crumbs. ๐
and now, the operations you can run on that file descriptor eventfd() gave you:
read(2) ๐
every successful read(2) returns an 8-byte integer. if your supplied buffer is less than 8 bytes, the read(2) fails with the error EINVAL. size matters, bestie.
the value that read(2) returns is in host byte order โ that is, the native byte order for integers on the host machine. ๐
and the semantics of read(2) depend on two things: whether the eventfd counter currently has a nonzero value, and whether the EFD_SEMAPHORE flag was specified when you created the eventfd file descriptor. so let's break it down:
- if
EFD_SEMAPHOREwas not specified and the eventfd counter has a nonzero value, then aread(2)returns 8 bytes containing that value, and the counter's value is reset to zero. gone. wiped. ๐งผ - if
EFD_SEMAPHOREwas specified and the eventfd counter has a nonzero value, then aread(2)returns 8 bytes containing the value 1, and the counter's value is decremented by 1. one at a time, queen. ๐ - if the eventfd counter is zero at the time of the call to
read(2), then the call either blocks until the counter becomes nonzero (at which point theread(2)proceeds as described above), or it fails with the errorEAGAINif the file descriptor has been made nonblocking. choose your fighter. ๐ญ
write(2) โ๏ธ
a write(2) call adds the 8-byte integer value supplied in its buffer to the counter. the maximum value that may be stored in the counter is the largest unsigned 64-bit value minus 1 (i.e., 0xfffffffffffffffe). if the addition would push the counter's value past that maximum, then the write(2) either blocks until a read(2) is performed on the file descriptor, or fails with the error EAGAIN if the file descriptor has been made nonblocking.
and a write(2) fails with the error EINVAL if the size of the supplied buffer is less than 8 bytes, or if you try to write the value 0xffffffffffffffff. don't do that. ๐ซ
poll(2), select(2) (and similar) ๐
the returned file descriptor supports poll(2) (and analogously epoll(7)) and select(2), like this:
- the file descriptor is readable (that's the
select(2)readfdsargument, and thepoll(2)POLLINflag) if the counter has a value greater than 0. โญ - the file descriptor is writable (the
select(2)writefdsargument, thepoll(2)POLLOUTflag) if it's possible to write a value of at least"1"without blocking. - if an overflow of the counter value was detected, then
select(2)indicates the file descriptor as being both readable and writable, andpoll(2)returns aPOLLERRevent. as noted above,write(2)can never overflow the counter. however, an overflow can occur if2^64eventfd "signal posts" were performed by the KAIO subsystem (theoretically possible, but practically unlikely โ literally do not worry about it ๐). if an overflow has occurred, thenread(2)will return that maximumuint64_tvalue (i.e.,0xffffffffffffffff).
also, the eventfd file descriptor supports the other file-descriptor multiplexing APIs too: pselect(2) and ppoll(2). multitasking icon. ๐
close(2) ๐ช
when you don't need the file descriptor anymore, you should close it. once all file descriptors associated with the same eventfd object have been closed, the kernel frees the resources for that object. it's over, it's done, cleanup era. โจ
and one more thing: a copy of the file descriptor created by eventfd() is inherited by the child produced by fork(2), and that duplicate file descriptor is associated with the same eventfd object. file descriptors created by eventfd() are preserved across execve(2), unless the close-on-exec flag has been set. family business. ๐ซถ
๐ RETURN VALUE ๐
on success, eventfd() returns a new eventfd file descriptor. on error, -1 is returned and errno is set to indicate the error. that's it, that's the deal.
๐ญ ERRORS ๐ญ
EINVALโ an unsupported value was specified inflags.EMFILEโ the per-process limit on the number of open file descriptors has been reached.ENFILEโ the system-wide limit on the total number of open files has been reached.ENODEVโ could not mount (internal) anonymous inode device.ENOMEMโ there was insufficient memory to create a new eventfd file descriptor.
๐ฆ ATTRIBUTES ๐ฆ
for an explanation of the terms used in this section, see attributes(7).
- Interface:
eventfd() - Attribute: Thread safety
- Value:
MT-Safe
(that's the good one, btw. MT-Safe. slay. ๐ )
โญ VERSIONS โญ
๐ C library/kernel differences
there are two underlying Linux system calls: eventfd() and the more recent eventfd2(). the former system call does not implement a flags argument. the latter system call does implement the flags values described above. and the glibc wrapper function will use eventfd2() wherever it's available. sneaky, but helpful. ๐ง
๐ Additional glibc features
the GNU C library defines an additional type and two functions that try to abstract away some of the details of reading and writing on an eventfd file descriptor:
typedef uint64_t eventfd_t;
int eventfd_read(int fd, eventfd_t *value);
int eventfd_write(int fd, eventfd_t value);
these functions perform the read and write operations on an eventfd file descriptor, and they return 0 if the correct number of bytes was transferred, or -1 otherwise. ๐ฏ
๐ STANDARDS ๐
Linux, GNU.
๐ฐ๏ธ HISTORY ๐ฐ๏ธ
eventfd()โ Linux 2.6.22, glibc 2.8.eventfd2()โ Linux 2.6.27 (see VERSIONS). since glibc 2.9, theeventfd()wrapper will employ theeventfd2()system call, if it's supported by the kernel.
๐ NOTES ๐
applications can use an eventfd file descriptor instead of a pipe (see pipe(2)) in all cases where a pipe is used simply to signal events. and here's the tea: the kernel overhead of an eventfd file descriptor is much lower than that of a pipe, and you only need one file descriptor (versus the two required for a pipe). half the fds, double the vibe. ๐ฅ
when it's used in the kernel, an eventfd file descriptor can provide a bridge from kernel to user space, allowing functionalities like KAIO (kernel AIO) to signal to a file descriptor that some operation is complete. so thoughtful. ๐ซถ
another key point about an eventfd file descriptor: it can be monitored just like any other file descriptor using select(2), poll(2), or epoll(7). which means an application can simultaneously monitor the readiness of "traditional" files and the readiness of other kernel mechanisms that support the eventfd interface. (without the eventfd() interface, these mechanisms could not be multiplexed via select(2), poll(2), or epoll(7) โ so like, genuinely a big deal.) ๐
and finally: the current value of an eventfd counter can be viewed via the entry for the corresponding file descriptor in the process's /proc/pid/fdinfo directory. see proc(5) for further details. snooping, but make it sanctioned. ๐
โจ EXAMPLES โจ
okay bestie this is the part where it all comes together ๐ so we've got a whole lil program that makes an eventfd file descriptor and then forks itself to create a child process. here's the vibe: the parent takes a lil nap (just a quick sleep, she's tired, it's fine ๐ด), and while she's snoozing, the child goes feral and writes every single integer you passed in on the command line right into that eventfd file descriptor. then once the parent wakes up from her beauty rest, she reads from the eventfd file descriptor. iconic behaviour, truly.
and here's what an actual run of the program looks like in your shell, showing sample run ๐ :
$ ./a.out 1 2 4 7 14
Child writing 1 to efd
Child writing 2 to efd
Child writing 4 to efd
Child writing 7 to efd
Child writing 14 to efd
Child completed write loop
Parent about to read
Parent read 28 (0x1c) from efd
notice how all those numbers just accumulated into28?1 + 2 + 4 + 7 + 14 = 28and28in hex is0x1c. that's the whole eventfd counter thing doing its thing, no cap ๐ฅ
Program source
here's the whole thing, copy it, run it, love it ๐ซถ:
#include <err.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int efd;
uint64_t u;
ssize_t s;
if (argc < 2) {
fprintf(stderr, "Usage: %s <num>...\n", argv[0]);
exit(EXIT_FAILURE);
}
efd = eventfd(0, 0);
if (efd == -1)
err(EXIT_FAILURE, "eventfd");
switch (fork()) {
case 0:
for (size_t j = 1; j < argc; j++) {
printf("Child writing %s to efd\n", argv[j]);
u = strtoull(argv[j], NULL, 0);
/* strtoull() allows various bases */
s = write(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
err(EXIT_FAILURE, "write");
}
printf("Child completed write loop\n");
exit(EXIT_SUCCESS);
default:
sleep(2);
printf("Parent about to read\n");
s = read(efd, &u, sizeof(uint64_t));
if (s != sizeof(uint64_t))
err(EXIT_FAILURE, "read");
printf("Parent read %w64u (%#w64x) from efd\n", u, u);
exit(EXIT_SUCCESS);
case -1:
err(EXIT_FAILURE, "fork");
}
}
a couple lil notes so you're not confused, bestie ๐ the strtoull(argv[j], NULL, 0) call passes a base of 0 because strtoull() allows various bases โ so like it'll figure out hex, octal, decimal, whatever you throw at it, she's flexible ๐โโ๏ธ. and the format string in that last printf is %w64u and %#w64x because we're printing a uint64_t, so you need the width-aware versions (<inttypes.h> is the one providing those). also yeah, the fds are shared across the fork โ that's the entire point, the child and parent are literally holding hands through the same efd ๐ซถโจ
๐ SEE ALSO ๐
futex(2)pipe(2)poll(2)read(2)select(2)signalfd(2)timerfd_create(2)write(2)epoll(7)sem_overview(7)
and that's the whole page, bestie!! ๐ this one came from the Linux man-pages, version 6.20, dated 2026-02-16 โ go forth and eventfd responsibly, mwah ๐๐โจ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: GPL-2.0-or-later
- Copyright 2008, Michael Kerrisk <tk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms