事件编程之eventfd

介绍

eventfd是一个内核实现的线程间/进程间通讯的事件通知机制

man

https://man7.org/linux/man-pages/man2/eventfd.2.html

int eventfd(unsigned int initval, int flags);
支持read/write/poll/select

       EFD_CLOEXEC (since Linux 2.6.27)
              Set the close-on-exec (FD_CLOEXEC) flag on the new file
              descriptor.  See the description of the O_CLOEXEC flag in
              open(2) for reasons why this may be useful.

       EFD_NONBLOCK (since Linux 2.6.27)
              Set the O_NONBLOCK file status flag on the open file
              description (see open(2)) referred to by the new file
              descriptor.  Using this flag saves extra calls to fcntl(2)
              to achieve the same result.

       EFD_SEMAPHORE (since Linux 2.6.30)
              Provide semaphore-like semantics for reads from the new
              file descriptor.  See below.

exam

           $ ./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
       #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 %"PRIu64" (%#"PRIx64") from efd\n", u, u);
               exit(EXIT_SUCCESS);

           case -1:
               err(EXIT_FAILURE, "fork");
           }
       }

使用

创建eventfd并获取一个fd描述符
线程A:对fd写入值
线程B:对fd读取值

读写的过程是read/write堵塞操作,会有调度。

原理

系统调用 eventfd2
通过select/poll等io多路并发检测,通过read/write这种堵塞调度来实现事件的等待和执行。

内核源码:fs/eventfd.c
创建
image
操作
image

比较简单,就是基于等待队列的实现,而且也只能存val,不能存别的东西。
本质上是系统提供的一种简单通讯机制。

应用场景

高性能编程
不同线程间的同步等待可以用eventfd来简洁实现

posted @ 2026-05-07 15:09  蓝天上的云℡  阅读(5)  评论(0)    收藏  举报