如何检查stdin是不是仍然打开而不阻塞?

Posted

技术标签:

【中文标题】如何检查stdin是不是仍然打开而不阻塞?【英文标题】:How to check if stdin is still opened without blocking?如何检查stdin是否仍然打开而不阻塞? 【发布时间】:2010-12-08 07:58:08 【问题描述】:

我需要用纯 C 编写的程序在标准输入关闭时停止执行。

在程序主循环中完成了无限期的工作,我无法在那里使用阻塞检查(如getc())(没有数据应该到达标准输入 - 它只是在未知时间内保持打开状态)。

我打算使用描述的功能来实现托管在 inetd、xinetd 或其类似物的网络守护程序 - 它应该在连接保持打开状态时在标准输出上发出数据,并在连接关闭时正确完成工作。现在我的程序被托管服务杀死了,因为它在连接终止后不会停止。

我想知道fctntl()O_NONBLOCK 标志应用于标准输入描述符是否允许我在非阻塞模式下使用read() 函数?我应该以某种方式使用select() 吗?

附:数据不是假设的,但可能会到达标准输入。一种非阻塞读出的方式将是该问题的答案。

【问题讨论】:

【参考方案1】:

select() 完全符合您的要求:表示对文件描述符(文件、套接字等)的操作(在本例中为读取)不会阻塞。

#include <stdio.h>
#include <sys/select.h>

int is_ready(int fd) 
    fd_set fdset;
    struct timeval timeout;
    int ret;
    FD_ZERO(&fdset);
    FD_SET(fd, &fdset);
    timeout.tv_sec = 0;
    timeout.tv_usec = 1;
    //int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
     struct timeval *timeout);
    return select(fd+1, &fdset, NULL, NULL, &timeout) == 1 ? 1 : 0;

您现在可以在使用前检查文件描述符,例如为了清空文件描述符:

void empty_fd(int fd) 
    char buffer[1024];
    while (is_ready(fd)) 
        read(fd, buffer, sizeof(buffer));
    

在你的情况下,使用 fileno(stdin) 来获取 stdin 的文件描述符:

if (is_ready(fileno(stdin))) 
    /* read stuff from stdin will not block */

【讨论】:

poll 也正是 OP 想要的,它的接口比select 在处理一小部分固定的文件描述符时要容易一些。【参考方案2】:

我想知道是否 fctntl() 将 O_NONBLOCK 标志应用于标准输入描述符将允许我在非阻塞模式下使用 read() 函数?

使用 O_NONBLOCK 运行标准输入比选择具有优势。选择说有一些数据,但没有多少。有时您希望获取所有可用数据,但不阻塞,无论队列中有多少。为每个角色运行 select 似乎很忙…… O_NONBLOCK 对我不起作用。它是一个内部标志,不会在大多数 tty 驱动程序中公开。

查看 ioctl(..., FIONBIO)。它似乎完成了工作。

【讨论】:

【参考方案3】:

我不确定你是否可以在标准输入上设置 O_NONBLOCK,但select()poll() 肯定会完成这项工作。

【讨论】:

是的,您可以在标准输入上设置 O_NONBLOCK。但是,如果您的标准输入是从另一个程序通过管道传输的,则该程序可能无法很好地处理其出站文件描述符变为非阻塞。【参考方案4】:

feof(stdin) 有什么问题?

【讨论】:

如果您不读出所有数据,那将无法正常工作。如何在不阻塞操作的情况下读出所有数据(例如垃圾)? EOF 可以被注入到一个开放的标准输入中(例如,通过 Linux 上的 Ctrl-D)。这并不意味着标准输入已关闭。【参考方案5】:

是的,您可以使用select(超时时间为零)。但是,您不需要将文件描述符设置为非阻塞 - 如果 select 告诉您文件描述符是可读的,那么它上面的 read 肯定不会阻塞。

因此,偶尔使用select 轮询文件描述符0,如果它可读,则read 它。如果read 返回 0,则表示已关闭。

【讨论】:

以上是关于如何检查stdin是不是仍然打开而不阻塞?的主要内容,如果未能解决你的问题,请参考以下文章

在没有flush()和新行的子进程输出上进行非阻塞读取

如何在C中检查stdin是不是为空

如何检查标准输入是不是有一些数据?

如何检查标准输入在 TCL 中是不是可读?

如何检查光标是不是存在(打开状态)

如何在 Perl 中测试 STDIN 而不会阻塞?