为什么在这种情况下 stdin fd 还没有准备好

Why in this case the stdin fd is not ready

根据 Linux Programmer's Manualpoll 可以等待一组文件描述符中的一个准备好执行 I/O。

根据我的理解,如果我将POLLIN添加到eventspoll将return与一个> 0整数,当至少有一个可以阅读的 fd。

考虑以下代码,在这段代码中,我希望程序在我键入字符 \n 后立即回显我的输入。

int main(){
    char buffer[maxn];
    while (true) {
        struct pollfd pfd[1];
        std::memset(pfd, 0, sizeof pfd);

        pfd[0].fd = STDIN_FILENO;
        pfd[0].events = POLLIN;

        int ret = poll(pfd, 1, 1000);

        if (ret < 0) {
        }
        else if (ret == 0) {
        }
        else {
            if ((pfd[0].revents & POLLIN) == POLLIN) {
                int n;
                n = fscanf(stdin, "%s", &buffer);
                if(n > 0){
                    printf("data from stdin: %s\n", buffer);
                }
            }else if((pfd[1].revents & POLLHUP) == POLLHUP){
                break;
            }
        }
    }
}

当我输入时

aa bb cc dd

我认为 fscanf 没有从标准输入中检索所有数据,因为它只读取 aa。所以当循环重新开始时,stdin 的 fd 应该仍然准备就绪。结果,(pfd[0].revents & POLLIN) == POLLIN 仍然存在,所以我认为我们可以看到以下输出

data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd

然而,实际上只打印了第一行。我在这里感到奇怪,我认为这与 epollEdge-triggered mode 类似。然而,poll是水平触发的。

那么你能解释一下为什么 fscanf 会发生这种情况吗?

轮询在文件 描述符 级别工作,而 fscanf 在更高文件 句柄 级别工作。

在较高级别,C 运行时库可以自由缓存输入流,这种方式会影响您在较低级别看到的内容。

例如(这可能就是这里发生的情况),当你第一次 fscanf 你的单词 aa 时,整个 是从文件描述符和缓存,在第一个单词返回给你之前。

随后的 fscanf(没有中间的 poll)将首先检查缓存以获取下一个单词,如果不存在,它将返回到文件描述符以获得更多意见。

不幸的是,您在执行此操作之前检查轮询事件会导致问题。就文件 descriptor 级别而言,entire 行已被您的第一个 fscanf 读取,因此没有进一步的输入可用 -因此,poll 将等待此类信息 可用。

如果您更改:

,您可以看到实际效果
n = fscanf(stdin, "%s", buffer);

进入:

n = read(STDIN_FILENO, buffer, 3);

并将 printf 更改为:

printf("data from stdin: %*.*s\n", n, n, buffer);

在这种情况下,您 do 会在您按下 ENTER 键后立即获得您期望的输出:

data from stdin: aa
data from stdin: bb
data from stdin: cc
data from stdin: dd

请记住示例代码最多读取 三个 个字符(如 aa<space>)而不是一个单词。更多的是说明问题是什么而不是给你解决方案(匹配你的问题"Can you explain why this happens?")。

解决方案不混合描述符和句柄I/O当后者的缓存会影响前者时。