如何检查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是不是仍然打开而不阻塞?的主要内容,如果未能解决你的问题,请参考以下文章