0

请考虑以下程序:

#define _GNU_SOURCE
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

int verify(int result, const char *msg) {
    if( result>=0 )
        return result;

    perror(msg);
    abort();

    return -1;
}

void writepipe( int fd, int num_bytes, const char *msg ) {
    unsigned char buffer[num_bytes];
    ssize_t num_written = verify( write(fd, buffer, num_bytes), msg );
    assert( num_written==num_bytes );
}

void readpipe( int fd, int num_bytes, const char *msg ) {
    unsigned char buffer[num_bytes];
    ssize_t num_read = verify( read(fd, buffer, num_bytes), msg );
    assert( num_read==num_bytes );
}

int main() {
    int pipefds[2];
    verify( pipe2(pipefds, O_NONBLOCK), "pipe creation failed" );

    int epollfd = verify(epoll_create1(0), "epoll creation failed");

    struct epoll_event evt;
    evt.events = EPOLLIN|EPOLLET;
    evt.data.u64 = 17;
    verify( epoll_ctl( epollfd, EPOLL_CTL_ADD, pipefds[0], &evt ), "epoll_add failed" );

    int num_events = verify( epoll_wait(epollfd, &evt, 1, 0), "epoll_wait failed" );
    assert(num_events == 0);

    writepipe( pipefds[1], 12, "initial filling of pipe" );

    num_events = verify( epoll_wait(epollfd, &evt, 1, 0), "epoll_wait failed" );
    assert(num_events == 1);
    assert(evt.data.u64 == 17);

    num_events = verify( epoll_wait(epollfd, &evt, 1, 0), "epoll_wait failed" );
    assert(num_events == 0);

    readpipe( pipefds[0], 12, "clean the data" );

    num_events = verify( epoll_wait(epollfd, &evt, 1, 0), "epoll_wait failed" );
    assert(num_events == 0);

    writepipe( pipefds[1], 3, "write no trigger" );

    num_events = verify( epoll_wait(epollfd, &evt, 1, 0), "epoll_wait on unarmed fd" );
    assert(num_events == 0);

    return 0;
}

最后一个断言失败。

因为我们从来没有EPOLLET从 epoll 中读取 an,所以我期待最后一个epoll_wait返回 0。相反,我得到了 1。

这是为什么?

来自 Ubuntu 16.10 的内核 4.13.0-39-generic。

4

1 回答 1

1

一个迟到的答案,但可能仍然对其他人有帮助。

您在最后一次 epoll_wait 调用中假设 fd 没有武装。不是这种情况。如果你真的希望它没有武装,你可以使用 EPOLLONESHOT。在它触发一次后,您必须为 epoll 重新准备它。您可能还假设第二次写入不会导致触发 epoll。这个假设也是错误的。EPOLLET 只保证只要 FD 上没有变化就不会再次触发 EPOLLET。管道上的写入触发了更改,因此触发了 epoll(不一定是人们期望发生的)。

这样做的原因是边缘触发模式仅在受监视的文件描述符发生更改时才传递事件。

来源: http: //man7.org/linux/man-pages/man7/epoll.7.html

我真的不知道您所说的“我们从未读过 EPOLLET”是什么意思,您的意思是表示所有数据都已被读取的 EAGAIN 吗?这实际上与您的问题无关。你完全清空管道。所以下一次读取会导致 EAGAIN,但这不会改变上面提到的行为。即使不读取数据,第二次写入也会触发 epoll。如果文件描述符没有变化,检查 EAGAIN 只是为了确保我们完全读取所有数据。

于 2018-12-06T08:57:12.450 回答