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 the O_CLOEXEC flag in open(2), it's all explained there. no cap, it's important.
  • EFD_NONBLOCK (since Linux 2.6.27) โ€” sets the O_NONBLOCK file status flag on the open file description (see open(2)) that the new file descriptor points at. using this saves you from making extra fcntl(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_SEMAPHORE was not specified and the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing that value, and the counter's value is reset to zero. gone. wiped. ๐Ÿงผ
  • if EFD_SEMAPHORE was specified and the eventfd counter has a nonzero value, then a read(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 the read(2) proceeds as described above), or it fails with the error EAGAIN if 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) readfds argument, and the poll(2) POLLIN flag) if the counter has a value greater than 0. โญ
  • the file descriptor is writable (the select(2) writefds argument, the poll(2) POLLOUT flag) 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, and poll(2) returns a POLLERR event. as noted above, write(2) can never overflow the counter. however, an overflow can occur if 2^64 eventfd "signal posts" were performed by the KAIO subsystem (theoretically possible, but practically unlikely โ€” literally do not worry about it ๐Ÿ™ˆ). if an overflow has occurred, then read(2) will return that maximum uint64_t value (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 in flags.
  • 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, the eventfd() wrapper will employ the eventfd2() 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 into 28? 1 + 2 + 4 + 7 + 14 = 28 and 28 in hex is 0x1c. 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 ๐Ÿ‘€


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
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms