手动输入时,Poll 对 stdin 起作用,但当输入通过管道传输且未重定向时则不起作用

Poll works on stdin when entering manually, but not when input is piped and not redirected

考虑这个 C 程序:

#include <poll.h>
#include <stdio.h>
#include <unistd.h>

#define TIMEOUT 500 // 0.5 s
#define BUF_SIZE 512

int fd_can_read(int fd, int timeout) {
    struct pollfd pfd;

    pfd.fd = fd;
    pfd.events = POLLIN;

    if (poll(&pfd, 1, timeout)) {
        if (pfd.revents & POLLIN) {
            return 1;
        }
    }

    return 0;
}

int main(int argv, char **argc) {
    int fd;
    size_t bytes_read;
    char buffer[BUF_SIZE];

    fd = STDIN_FILENO;

    while (1) {
        if (fd_can_read(fd, TIMEOUT)) {
            printf("Can read\n");
            bytes_read = read(fd, buffer, sizeof(buffer));

            printf("Bytes read: %zu\n", bytes_read);
        }
        else {
            printf("Can't read\n");
        }
    }
}

它尝试轮询给定的文件描述符(在本例中是 stdin 的 fd),并在它可供读取时尝试从中读取。这是一个名为 "input":

的示例输入文件
stuff to be read

假设我 运行 程序,提供一些输入并关闭它:

./a.out
test
Can read
Bytes read: 5
Can't read
Can't read
...

所以让我们尝试通过 piping/redirecting 将文件的内容读取到我程序的 stdin 中:

cat input | ./a.out # Or ./a.out < input
Bytes read: 0
Can read
Bytes read: 0
Can read
...

现在,轮询 returns 立即(不等待超时到 运行 结束),并给出我没有预料到的结果。我知道 poll() 不能正确处理文件,但如果我没记错的话,我不是在读取文件。

问题是poll (just like select) only tell you that a call to e.g. read不会阻塞。它不会告诉你是否真的有什么可读的。

如果你阅读 the read manual page 你会看到当它 returns 0 时它意味着 文件结束 (或连接关闭套接字)。

poll 告诉你的是 read 可以无阻塞地调用,而 read 通过返回 0 告诉你的是没有什么更多的阅读。

你会得到类似的 "false positive" 通过按文件结束快捷键(默认情况下 Ctrl-D POSIX 系统像 Linux) 对于非管道或 -redirected 输入示例。