为什么在这种情况下read()块?(linux epoll)

时间:2015-04-19 07:47:26

标签: c linux unix io epoll

我是unix programming的新手,今天我正在尝试epoll,但遇到了问题。

level-triggered模式下,我认为包含Ctrl-D的每个新输入事件都会导致epoll_wait返回。它工作正常。但是,当我输入aaa之类的内容时,请使用Ctrl-Dread块。当我输入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返回-1,但read()仍然告诉可读事件。但如果我只输入Ctrl-Dread()会返回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

2 个答案:

答案 0 :(得分:1)

据我了解行为:

如果您输入&#39; ENTER&#39;而不是ctrl-D,则报告4个事件与CTRL-D一样。我们看到ascii代码:10代表换行。使用CTRL-D read阻止。

CTRL-D并不发出EOF信号,而是将目前输入的数据清除掉。 CTRL-D本身被认为是一个事件。但实际上没有数据可以提取出来 FD。鉴于套接字阻塞,我们最终会遇到read在另一组事件发生之前没有返回的情况。

现在,如果CTRL-D是第一个事件,则会将read表示为零。发出EOF条件信号。如果有东西需要刷新,就不会发生这种情况。

当您使套接字无阻塞时,CTRL-D read返回-1,并将errno设置为EAGAIN。这意味着现在无法读取数据。稍后试试&#39;。

答案 1 :(得分:0)

从手册(epoll_ctl):

EPOLLERR
    Error condition happened on the associated file descriptor.
    epoll_wait(2) will always wait for this event;
    it is not necessary to set it in events.

如您所见,始终会监控错误情况。