linux手册翻译——eventfd(2)

2021-06-18  本文已影响0人  蟹蟹宁

\color{#A00000}{NAME}
eventfd - 为event notification创建文件描述符

\color{#A00000}{SYNOPSIS}

#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);

\color{#A00000}{DESCRIPTION}

eventfd() 创建了一个“eventfd 对象”,它可以被用户空间应用程序用作事件等待/通知机制,内核可以将事件通知用户空间应用程序。 该对象包含一个由内核维护的无符号 64 位整数 (uint64_t) 计数器。 此计数器使用参数 initval 中指定的值进行初始化。
eventfd() 将返回一个文件描述符,指向“eventfd 对象”。
参数 flag 可以是以下值按位或运算:

在 Linux 2.6.26 之前的版本中,flags 参数未使用,必须指定为零。
可以对 eventfd() 返回的文件描述符执行以下操作:

read(2)

成功读取会返回一个 8 字节整数。 如果提供的缓冲区的大小小于 8 字节,则 read(2) 失败并显示错误 EINVAL。
read(2) 返回的值是主机字节顺序,即主机上整数的本机字节顺序。换句话说,返回的值可以直接转化为整数。
read(2) 的语义取决于 eventfd 计数器当前是否具有非零值以及在创建 eventfd 文件描述符时是否指定了 EFD_SEMAPHORE 标志:

write(2)

write(2) 调用将其缓冲区中提供的 8 字节整数值添加到计数器(计数器的值加上write提供的值)。计数器的最大值为2^64-1(即0xffffffffffffffffe),如果相加的结果超过最大值,那么将阻塞write(),直到执行read(),当然,如果设置了EFD_NONBLOCK flags,则调用失败并返回错误 EAGAIN

poll(2), select(2),epoll(7)

eventfd 文件描述符还支持其他文件描述符多路复用 API:pselect(2) 和 ppoll(2)。

close(2)

当不再需要文件描述符时,应将其关闭。 当与同一个 eventfd 对象关联的所有文件描述符都已关闭时,内核会释放对象的资源。

由 eventfd() 创建的文件描述符的副本由 fork(2) 生成的子进程继承。 重复的文件描述符与相同的 eventfd 对象相关联。 除非设置了 close-on-exec 标志,否则 eventfd() 创建的文件描述符将在 execve(2) 中保留。

\color{#A00000}{RETURN VALUE}
成功时, eventfd() 返回一个新的 eventfd 文件描述符。 出错时,返回 -1 并设置 errno 以指示错误。

\color{#A00000}{ERRORS}

\color{#A00000}{VERSIONS}
eventfd() is available on Linux since kernel 2.6.22. Working support is provided in glibc since version 2.8. The eventfd2() system call (see NOTES) is available on Linux since kernel 2.6.27. Since version 2.9, the glibc eventfd() wrapper will employ the eventfd2() system call, if it is supported by the kernel.

\color{#A00000}{ATTRIBUTES}

Interface Attribute Value
eventfd() Thread safety MT-Safe

\color{#A00000}{CONFORMING TO}
eventfd() and eventfd2() are Linux-specific.

\color{#A00000}{NOTES}
在管道仅用于发送事件信号的所有情况下,应用程序都可以使用 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。
\color{#A00000}{EXAMPLES}
下面的程序创建一个 eventfd 文件描述符,然后 fork 创建一个子进程。 当父进程短暂休眠时,子进程将程序命令行参数中提供的每个整数写入 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

源代码:

#include <sys/eventfd.h>
#include <unistd.h>
#include <inttypes.h>           /* Definition of PRIu64 & PRIx64 */
#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;
    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 (int 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 %"PRIu64" (%#"PRIx64") from efd\n", u, u);
        exit(EXIT_SUCCESS);

    case -1:
        handle_error("fork");
    }
}
上一篇 下一篇

猜你喜欢

热点阅读