EVENTFD - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-06-09
名称
eventfd-为事件通知创建文件描述符
语法
#包括
int eventfd(unsigned int initval,int标志);
说明
eventfd()创建一个" eventfd对象",它可由用户空间应用程序和内核将其用作事件等待/通知机制,以将事件通知用户空间应用程序。该对象包含一个由内核维护的无符号64位整数(uint64_t)计数器。使用参数initval中指定的值初始化该计数器。
作为其返回值,eventfd()返回一个新的文件描述符,该文件描述符可用于引用eventfd对象。
以下值可以按标志进行按位或运算,以更改eventfd()的行为:
- EFD_CLOEXEC(since Linux 2.6.27)
- 在新文件描述符上设置执行时关闭(FD_CLOEXEC)标志。有关为什么可能有用的原因,请参见open(2)中O_CLOEXEC标志的描述。
- EFD_NONBLOCK(since Linux 2.6.27)
- 在新文件描述符引用的打开文件描述(请参见open(2))上设置O_NONBLOCK文件状态标志。使用此标志可以节省对fcntl(2)的额外调用,以实现相同的结果。
- EFD_SEMAPHORE(since Linux 2.6.30)
- 为从新文件描述符读取的内容提供类似信号量的语义。见下文。
在2.6.26版之前的Linux中,flags参数未使用,必须指定为零。
可以对eventfd()返回的文件描述符执行以下操作:
- read(2)
- 每个成功的read(2)返回一个8字节的整数。如果提供的缓冲区的大小小于8个字节,则read(2)失败,并显示错误EINVAL。
- read(2)返回的值是主机字节顺序的,即主机上整数的本机字节顺序。
- The semantics of
read(2)
depend on whether the eventfd counter currently has a nonzero value
and whether the
EFD_SEMAPHOREflag was specified when creating the eventfd file descriptor:
- write(2)
- write(2)调用将其缓冲区中提供的8字节整数值添加到计数器。计数器中可存储的最大值是最大的无符号64位值减去1(即0xfffffffffffffffe)。如果相加会导致计数器的值超过最大值,则write(2)会阻塞直到对文件描述符执行read(2)为止,或者如果已使文件描述符成为非阻塞状态,则会失败并显示错误EAGAIN。
- 如果提供的缓冲区的大小小于8个字节,或者尝试写入值0xffffffffffffffff,则write(2)失败,并显示错误EINVAL。
- poll(2), select(2) (and similar)
- The returned file descriptor supports
poll(2)
(and analogously
epoll(7))and
select(2),as follows:
- *
- 如果计数器的值大于0,则文件描述符是可读的(select(2)readfds参数; poll(2)POLLIN标志)。
- *
- 如果可以写入至少为" 1"的值而不会阻塞,则文件描述符是可写的(select(2)writefds参数; poll(2)POLLOUT标志)。
- *
- 如果检测到计数器值溢出,则select(2)表示文件描述符是可读写的,并且poll(2)返回POLLERR事件。如上所述,write(2)永远不会使计数器溢出。但是,如果KAIO子系统执行了2 ^ 64个eventfd"信号发布",则可能会发生溢出(理论上是可能的,但实际上不太可能)。如果发生溢出,则read(2)将返回该最大uint64_t值(即0xffffffffffffffff)。
- eventfd文件描述符还支持其他文件描述符多路复用API:pselect(2)和ppoll(2)。
- close(2)
- 当不再需要文件描述符时,应将其关闭。关闭与同一eventfd对象关联的所有文件描述符后,内核将释放对象的资源。
由eventfd()创建的文件描述符的副本由fork(2)产生的子代继承。重复的文件描述符与相同的eventfd对象关联。除非已设置close-on-exec标志,否则将由execve(2)保留由eventfd()创建的文件描述符。
返回值
成功后,eventfd()返回一个新的eventfd文件描述符。发生错误时,将返回-1并将errno设置为指示错误。
错误说明
- EINVAL
- 在标志中指定了不支持的值。
- EMFILE
- 已达到打开文件描述符数量的每个进程限制。
- ENFILE
- 已达到系统范围内打开文件总数的限制。
- ENODEV
- 无法安装(内部)匿名inode设备。
- ENOMEM
- 没有足够的内存来创建新的eventfd文件描述符。
版本
从内核2.6.22开始,eventfd()在Linux上可用。从2.8版开始,glibc提供了工作支持。从内核2.6.27开始,eventfd2()系统调用(请参阅注意)在Linux上可用。从2.9版开始,如果内核支持,glibc eventfd()包装器将使用eventfd2()系统调用。
属性
有关本节中使用的术语的说明,请参见attribute(7)。
Interface | Attribute | Value |
eventfd() | Thread safety | MT-Safe |
遵循规范
eventfd()和eventfd2()特定于Linux。
备注
在仅使用管道来表示事件的所有情况下,应用程序都可以使用eventfd文件描述符而不是管道(请参见pipe(2))。 eventfd文件描述符的内核开销远低于管道的内核开销,并且仅需要一个文件描述符(而不是管道需要的两个文件描述符)。
当在内核中使用时,eventfd文件描述符可以提供从内核到用户空间的桥梁,例如,允许诸如KAIO(内核AIO)之类的功能向文件描述符发出信号,表明某些操作已完成。
关于eventfd文件描述符的一个关键点是,可以像其他任何文件描述符一样使用select(2),poll(2)或epoll(7)对其进行监视。这意味着应用程序可以同时监视"传统"文件的准备情况和支持eventfd接口的其他内核机制的准备情况。 (没有eventfd()接口,无法通过select(2),poll(2)或epoll(7)复用这些机制。)
可以通过进程的/ proc / [pid] / fdinfo目录中相应文件描述符的条目来查看eventfd计数器的当前值。有关更多详细信息,请参见proc(5)。
C library/kernel differences
有两个底层的Linux系统调用:eventfd()和更新的eventfd2()。前一个系统调用未实现flags参数。后一个系统调用实现上述标记值。 glibc包装器函数将在可用的地方使用eventfd2()。
Additional glibc features
GNU C库定义了一个附加类型,以及两个函数,这些函数试图抽象化对eventfd文件描述符的读写细节:
typedef uint64_t eventfd_t; int eventfd_read(int fd, eventfd_t *value); int eventfd_write(int fd, eventfd_t value);
该函数对eventfd文件描述符执行读取和写入操作,如果传输了正确的字节数,则返回0,否则返回-1。
示例
以下程序创建一个eventfd文件描述符,然后进行分叉以创建一个子进程。父项短暂睡眠时,子项将程序命令行参数中提供的每个整数写入eventfd文件描述符。父级完成睡眠后,它将从eventfd文件描述符中读取。
以下shell会话显示了该程序的示例运行:
$ ./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
Program source
#include <sys/eventfd.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* Definition of uint64_t */ #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) { int efd, j; 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) handle_error("eventfd"); switch (fork()) { case 0: for (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)) handle_error("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)) handle_error("read"); printf("Parent read %llu (0x%llx) from efd\n", (unsigned long long) u, (unsigned long long) u); exit(EXIT_SUCCESS); case -1: handle_error("fork"); } }
另外参见
futex(2),pipe(2),poll(2),read(2),select(2),signalfd(2),timerfd_create(2),write(2),epoll(7),sem_overview(7)
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。