c - 为什么在这种情况下 read() 会阻塞?(linux epoll)

标签 c linux unix io epoll

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

level-triggered模式下,我认为每个新的输入事件包括Ctrl-D都会导致epoll_wait返回。它工作正常。但是,当我输入类似 aaa 的内容时,接着是 Ctrl-Dread 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-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

最佳答案

据我了解这种行为:

如果您输入“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/

相关文章:

objective-c - 如何查明二进制文件是在哪个操作系统中使用终端编译的?

linux - 无法 scp 到 AWS

bash - 从 bash 脚本启动一个新的进程组

bash - 遍历数组并执行存储在变量中的命令

c - 在 *nix 系统上的 C 中,使用 $PATH 或 "which"找到二进制文件的绝对路径?

检查有效整数

c - 优化代码(检查 mod ==0 是否为大 no 10^18)

linux - 使用 getopts (bash) 的多个选项参数

c++ - 从 C、C++ 在 Linux 中发出系统命令

c - 错误的 TCP 客户端实现