EPOLLET行为不正确?

时间:2018-05-10 18:02:31

标签: linux epoll epollet

请考虑以下计划:

#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;
}

最后一个断言失败。

由于我们从未从epoll中读过EPOLLET,我期待最后epoll_wait返回0.相反,我得到1.

为什么?

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

1 个答案:

答案 0 :(得分:0)

答案较晚,但也许对其他人还是有帮助的。

您假设在上一个epoll_wait调用中,fd处于未设防状态。不是这种情况。如果您实际上希望将其解除武装,则可以使用EPOLLONESHOT。触发一次后,您就必须重新整理它以进行epoll。 您可能还假设第二次写入不会导致epoll触发。这个假设也是错误的。只要FD上没有更改,EPOLLET仅保证EPOLLET不会再次被触发。管道上的写入会触发更改,因此会触发epoll(不一定是人们期望发生的事情)。

  

原因          因为这是边缘触发模式仅在          更改发生在受监视的文件描述符上。

来源:video

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