EPOLLONESHOT是否可以防止在对epoll_wait()的单次调用中返回单个描述符上的多个事件?

chb*_*er0 2 linux network-programming epoll

手册页上epoll_ctl(2)有关于该EPOLLONESHOT标志的说明:

Sets the one-shot behavior for the associated file descriptor.
This means that after an event is pulled out with
epoll_wait(2) the associated file descriptor is internally
disabled and no other events will be reported by the epoll
interface.  The user must call epoll_ctl() with EPOLL_CTL_MOD
to rearm the file descriptor with a new event mask.
Run Code Online (Sandbox Code Playgroud)

但是,尚不清楚是在epoll_wait插入事件数组之后还是在所有事件返回之后禁用该事件。

Fil*_*ves 6

的行为EPOLLONESHOT是,在成功调用epoll_wait(2)报告指定文件描述符的位置之后,将不会epoll_wait(2)在同一文件描述符上报告新事件,除非您使用显式重新激活它epoll_ctl(2)。您可以将其视为一种机制,以在文件描述符由返回时临时禁用epoll_wait(2)。

它不会阻止epoll_wait(2)在同一文件描述符的同一调用中返回多个事件-实际上,如果在调用时有多个事件可用,则将它们全部合并到的events字段中struct epoll_event,无论是否EPOLLONESHOT有效该文件描述符。

换句话说,EPOLLONESHOT控制在什么条件下对; 的调用中报告文件描述符epoll_wait(2)。它在事件聚合和检测中不起作用。

一个例子

下面的代码创建一对连接的套接字,并写入一端。sv[1]将可用于读写。如您所见,添加或删除EPOLLONESHOT对EPOLLIN和EPOLLOUT合并为一个事件的事实没有任何影响。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(void) {
    int efd = epoll_create(1);
    if (efd < 0) {
        perror("epoll_create(2) error");
        exit(EXIT_FAILURE);
    }

    int sv[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
        perror("socketpair(2) error");
        exit(EXIT_FAILURE);
    }

    const char str[] = "Hello, world!";
    size_t to_write = sizeof(str)-1;
    ssize_t written = write(sv[0], str, to_write);

    if (written != to_write) {
        if (written < 0) {
            perror("write(2) error");
        } else {
            fprintf(stderr, "short write detected: %zd/%zu\n", written, to_write);
        }
        exit(EXIT_FAILURE);
    }

    struct epoll_event epevent;
    epevent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; // Try to remove EPOLLONESHOT here
    epevent.data.fd = sv[1];

    if (epoll_ctl(efd, EPOLL_CTL_ADD, sv[1], &epevent) < 0) {
        perror("epoll_ctl(2) error");
        exit(EXIT_FAILURE);
    }

    struct epoll_event events_arr[16];
    int events = epoll_wait(efd, events_arr, sizeof(events_arr)/sizeof(*events_arr), -1);
    if (events < 0) {
        perror("epoll_wait(2) error");
        exit(EXIT_FAILURE);
    }

    int i;
    for (i = 0; i < events; i++) {
        printf("Event %d: ", i);
        if (events_arr[i].events & EPOLLIN)
            printf("EPOLLIN ");
        if (events_arr[i].events & EPOLLOUT)
            printf("EPOLLOUT ");
        printf("\n");
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)