[英]Why EPOLLOUT changes how EPOLLIN is handled?
關於事件是否合並的文檔尚不清楚,我的測試顯示它們在某些情況下但並非總是如此。
考慮man 7 epoll
:
因為即使使用邊緣觸發的epoll,在收到多個數據塊時也可以生成多個事件,調用者可以選擇指定EPOLLONESHOT標志......
以及問答部分:
Q7如果epoll_wait(2)呼叫之間發生多個事件,它們是否合並或單獨報告?
A7他們將合並。
我假設手冊中的第一個語句意味着你可以在從套接字讀取,數據包到達,你讀取它,然后另一個數據包到達的情況下收到多個EPOLLIN事件。 問答部分的答案是討論EPOLLIN和EPOLLOUT等不同事件。 如果我錯了,請糾正我。
我正在玩一些代碼,以便更好地理解epoll如何工作,並且根據是否設置了另一個事件,它似乎在相同類型的事件上表現不同。 更確切地說,如果我只等待EPOLLIN,則多個輸入會生成單個事件,但如果我等待EPOLLIN和EPOLLOUT,則多個輸入會生成多個事件。
這是我用來測試這個的代碼:
#include <stdio.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
struct epoll_event ev = {EPOLLIN|EPOLLOUT|EPOLLET};
int epoll = epoll_create1(0);
epoll_ctl(epoll, EPOLL_CTL_ADD, 0, &ev);
//async stdin
int flags = fcntl(0, F_GETFL);
flags |= O_NONBLOCK;
fcntl(0, F_SETFL, flags);
while(1){
struct epoll_event events[64];
int n = epoll_wait(epoll, events, 64, -1);
printf("Event count: %d\n", n);
if(events[0].events == EPOLLIN)
printf("EPOLLIN only\n\n");
else
if(events[0].events == (EPOLLIN|EPOLLOUT))
printf("EPOLLIN and EPOLLOUT\n\n");
else
printf("EPOLLOUT only\n\n");
char buffer[256];
read(0, buffer, 256);
sleep(1);
}
return 0;
}
按下返回后的輸出顯示接收到EPOLLIN和EPOLLOUT,此消息在按下返回時出現多次,然后它僅顯示正在生成的EPOLLOUT。
但是如果你在沒有EPOLLOUT標志的情況下編譯程序並多次按回車,則只會報告一次事件。
如果我刪除了read
調用,則在設置EPOLLOUT時繼續報告EPOLLIN,但在僅設置EPOLLIN時不會報告。
行為取決於它正在等待的事件或我的測試代碼有什么問題嗎? 如果它是依賴的,我可以放心它將來不會改變嗎?
我相信您正在觀察未定義行為的影響,因為您濫用了API。
具體來說,您將STDIN_FILENO
(即0
)傳遞給epoll_ctl
並要求在EPOLLOUT
上等待EPOLLOUT
。 可能發生的是操作系統試圖告訴您文件描述符的寫入方向有問題。
此外,使用邊緣觸發模式時,您應該繼續I / O,直到看到EAGAIN
。 當操作不再阻塞時, epoll_wait
調用將返回。
我修改了你的程序以使用套接字,並從套接字讀取直到EAGAIN
,它的行為與我預期的一樣。
在我的版本中,我創建了一個套接字對,以及一個從STDIN_FILENO
讀取並寫入該對套接字之一的線程。 該main
體循環然后epoll_wait
那另一個插座上。
當我啟動程序時,它會在第一次調用epoll_wait
返回報告可寫,但在下一次迭代時會阻塞:
Event count: 1 EPOLLOUT only
當我輸入輸入時,它報告可讀和可寫,然后按照預期在下一次迭代中在epoll_wait
上epoll_wait
:
asdf Event count: 1 EPOLLIN and EPOLLOUT
我使用的代碼如下。 一,線程:
static void * iothread (void *svp) {
int *sv = svp;
char buf[256];
ssize_t r;
again:
while ((r = read(0, buf, sizeof(buf))) > 0) {
ssize_t n = r;
const char *p = buf;
while (n > 0) {
r = write(sv[1], p, n);
if (r < 0) {
if (errno == EINTR) continue;
break;
}
n -= r;
p += r;
}
if (n > 0) break;
}
if (r < 0 && errno == EINTR) {
goto again;
}
close(sv[1]);
return NULL;
}
然后, main
體:
int main(int argc, char* argv[]) {
int sv[2];
struct epoll_event ev = {EPOLLIN | EPOLLOUT | EPOLLET};
int epoll = epoll_create1(0);
pthread_t t;
socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
pthread_create(&t, NULL, iothread, sv);
epoll_ctl(epoll, EPOLL_CTL_ADD, sv[0], &ev);
while(1){
struct epoll_event events[64];
int n = epoll_wait(epoll, events, 64, -1);
printf("Event count: %d\n", n);
if(events[0].events == EPOLLIN)
printf("EPOLLIN only\n\n");
else
if(events[0].events == (EPOLLIN|EPOLLOUT))
printf("EPOLLIN and EPOLLOUT\n\n");
else
printf("EPOLLOUT only\n\n");
char buffer[256];
ssize_t r;
again:
r = recv(sv[0], buffer, 256, MSG_DONTWAIT);
if (r > 0) goto again;
if (r < 0 && errno == EAGAIN) {
sleep(1);
continue;
}
break;
}
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.