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

12

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

程序主循环中有不确定的工作量,我无法使用阻塞检查(例如getc())(没有数据应该到达stdin - 它只是打开了未知时间)。

我打算在inetd、xinetd或它们的类比实现的网络守护进程中使用所述功能 - 当连接关闭时,它应在stdout上发出数据并正确完成工作。由于它无法在连接终止后停止,因此我的程序被托管服务终止。

我想知道是否可以将fctntl()与应用于stdin描述符的O_NONBLOCK标志结合使用,以便在非阻塞模式下使用read()函数?我是否应该以某种方式使用select()

P.S. 数据不应该但可能会到达stdin。非阻塞读取的方法将是问题的答案。

5个回答

8

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”更容易。 - ephemient

2
我想知道将O_NONBLOCK标志应用于stdin描述符的fctntl()函数是否允许我以非阻塞模式使用read()函数?
将O_NONBLOCK应用于stdin具有优于select的优点。Select表示有一些数据,但不知道有多少。有时您想获取所有可用数据,但不管队列中有多少数据都不想被阻塞。每个字符都运行select似乎是很繁忙的工作... O_NONBLOCK对我没有起作用。它是一个内部标志,在大多数tty驱动程序中不会被公开。
请查看ioctl(..., FIONBIO)。它似乎可以完成任务。

1

我不确定您是否可以在标准输入上设置O_NONBLOCK,但是select()poll()肯定可以完成任务。


是的,你可以在标准输入上设置 O_NONBLOCK。但是,如果你的标准输入是从另一个程序进行管道传输的话,那么该程序可能无法很好地处理其输出文件描述符变为非阻塞的情况。 - ephemient

0

feof(stdin) 有什么问题吗?


1
如果您不读取所有数据,那是行不通的。如何在不阻塞操作的情况下读取所有数据(包括垃圾数据)? - Basilevs
1
EOF可以注入到打开的stdin中(例如通过Linux上的Ctrl-D)。这并不意味着stdin已关闭。 - Kerrek SB

0

是的,您可以使用带有零超时的select。您不需要将文件描述符设置为非阻塞状态,如果select告诉您文件描述符可读,则对其进行read肯定不会阻塞。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接