我是 unix 编程的新手
,今天我正在尝试 epoll
但遇到了一个问题。
在level-triggered
模式下,我认为每个新的输入事件包括Ctrl-D
都会导致epoll_wait
返回。它工作正常。但是,当我输入类似 aaa
的内容时,接着是 Ctrl-D
,read
block 。当我键入 Ctrl-D
时,它没有。
你能解释一下发生了什么吗?
我是否应该在epoll_wait
完成并根据fd
准备就绪时读取所有数据?
谢谢!
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, const char *argv[]) {
// create event
struct epoll_event stdin_ev, events[10];
// set event
stdin_ev.events = EPOLLIN;
stdin_ev.data.fd = STDIN_FILENO;
// create epoll
int epfd = epoll_create(1), i, rcnt;
char c;
// set monitoring STDIN_FILENO
epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev);
while(1) {
int ret = epoll_wait(epfd, events, 1, 1000);
// timeout or failed
if(ret == 0) {
fprintf(stdout, "timeout\n");
continue;
} else if (ret < 0) {
perror("ret<0");
exit(EXIT_FAILURE);
}
// readable
fprintf(stdout, "%d event(s) happened...\n", ret);
for(i=0; i < ret; i++) {
if(events[i].data.fd == STDIN_FILENO &&\
events[i].events&EPOLLIN) {
// read a char
rcnt = read(STDIN_FILENO, &c, 1);
// if read 0 char, EOF?
if(rcnt != 1) {
fprintf(stdout, "read %d byte\n", rcnt);
continue;
}
// else print ascii
fprintf(stdout, "ascii code: %d\n", c);
}
}
}
close(epfd);
return 0;
}
输入:aaa
+Ctrl-D
,结果:
timeout
aaa // <-- `aaa`+`Ctrl-D`
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
^C // <-- read block here, `Ctrl-C` to kill
然后我尝试将 STDIN_FILENO
设置为非阻塞,我发现 epoll_wait
仍然告诉有一个可读事件,尽管 read()
返回 - 1.但是,如果我只是键入 Ctrl-D
,read()
将返回 0。
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int set_nonblock(int sfd) {
int flags, s;
flags = fcntl(sfd, F_GETFL, 0);
if(flags == -1) {
perror("fcntl");
return -1;
}
flags |= O_NONBLOCK;
s = fcntl(sfd, F_SETFL, flags);
if(s == -1) {
perror("fcntl");
return -1;
}
return 0;
}
int main(int argc, const char *argv[]) {
// create event
struct epoll_event stdin_ev, events[10];
// set event
stdin_ev.events = EPOLLIN;
stdin_ev.data.fd = STDIN_FILENO;
// create epoll
int epfd = epoll_create(1), i, rcnt;
char c;
// set nonblocking
if(set_nonblock(STDIN_FILENO) != 0) {
exit(EXIT_FAILURE);
};
// set monitoring STDIN_FILENO
epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev);
while(1) {
int ret = epoll_wait(epfd, events, 1, 1000);
// timeout or failed
if(ret == 0) {
fprintf(stdout, "timeout\n");
continue;
} else if (ret < 0) {
perror("ret<0");
exit(EXIT_FAILURE);
}
// readable
fprintf(stdout, "%d event(s) happened...\n", ret);
for(i=0;i < ret;i++) {
if(events[i].data.fd == STDIN_FILENO &&\
events[i].events&EPOLLIN) {
// read a char
rcnt = read(STDIN_FILENO, &c, 1);
// if read 0 char, EOF?
if(rcnt != 1) {
fprintf(stdout, "read %d byte\n", rcnt);
continue;
}
// else print ascii
fprintf(stdout, "ascii code: %d\n", c);
}
}
}
close(epfd);
return 0;
}
结果:
timeout
1 event(s) happened... // <-- `Ctrl-D`
read 0 byte // <-- read() -> 0
timeout
timeout
aaa // `aaa`+`Ctrl-D`
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
1 event(s) happened...
ascii code: 97
read -1 byte // `EPOLLIN` still happens.
timeout
^C
最佳答案
据我了解这种行为:
如果您输入“ENTER”而不是 ctrl-D,则与 CTRL-D 一样会报告 4 个事件。我们看到换行符的 ascii 代码:10。使用 CTRL-D read
block 。
CTRL-D 不是发出 EOF 信号,而是清除到目前为止输入的数据。 CTRL-D 本身被识别为一个事件。但实际上没有数据可以提取
fd。鉴于 socket 正在阻塞,我们最终会遇到 read
在另一组事件发生之前不会返回的情况。
现在如果 CTRL-D 是第一个事件,它会被识别为 read
并返回零。发出 EOF 条件信号。如果有要冲洗的东西,则不会发生这种情况。
当您使套接字成为非阻塞时,CTRL-D read
返回 -1,errno 设置为 EAGAIN。这意味着“现在没有要读取的数据”。等会再试'。
关于c - 为什么在这种情况下 read() 会阻塞?(linux epoll),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29727210/