[英]Incorrect EPOLLET behavior?
請考慮以下程序:
#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的Kernel 4.13.0-39-generic。
答案較晚,但可能對其他人還是有幫助的。
您在上一個epoll_wait調用中假定fd是未設防的。 不是這種情況。 如果您實際上希望將其解除武裝,則可以使用EPOLLONESHOT。 觸發一次后,您就必須重新整理它以進行epoll。 您可能還假設第二次寫入不會導致epoll觸發。 這個假設也是錯誤的。 只要FD上沒有更改,EPOLLET僅保證EPOLLET不會再次被觸發。 管道上的寫入會觸發更改,因此會觸發epoll(不一定是人們期望發生的事情)。
原因是邊緣觸發模式僅在受監視的文件描述符發生更改時才傳送事件。
來源: http : //man7.org/linux/man-pages/man7/epoll.7.html
我真的不知道您所說的“我們永遠不會讀取EPOLLET”是什么意思,您的意思是表示所有數據已被讀取的EAGAIN嗎? 這實際上與您的問題無關。 您完全清空了管道。 因此,下一次讀取將導致EAGAIN,但這不會更改上述行為。 即使您不讀取數據,第二次寫入也會觸發epoll。 檢查EAGAIN只是為了確保如果文件描述符上沒有任何更改,我們將完全讀取所有數據。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.