EVENTFD - Linux手册页

时间:2019-08-20 17:58:38  来源:igfitidea点击:

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_SEMAPHORE

flag was specified when creating the eventfd file descriptor:

*
如果未指定EFD_SEMAPHORE并且eventfd计数器具有非零值,则read(2)返回包含该值的8个字节,并且计数器的值将重置为零。
*
如果指定了EFD_SEMAPHORE并且eventfd计数器的值非零,则read(2)返回8个字节,其中包含值1,并且该计数器的值减1。
*
如果在调用read(2)时eventfd计数器为零,则该调用将阻塞直到计数器变为非零(此时,read(2)如上所述进行),否则将失败并返回错误EAGAIN文件描述符已设为非阻塞。
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)。

InterfaceAttributeValue
eventfd()Thread safetyMT-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/