我有一个C程序,它通过(Linux)管道从另一个程序接收数据。我希望如果在写入任何数据之前关闭管道,程序的行为会有所不同。
这样做的自然方法是尝试从管道中读取并检查是否得到EOF
,但是如果有任何可用的数据,这将消耗管道中的一些数据,并且(据我所知)没有办法将数据“放回”管道中。
我想检查管道是否为空的程序部分离我处理数据的地方很远,所以我宁愿在第一次读取之前不必保存数据。
有没有什么方法可以检查管道是否为空(read
将返回EOF
),而在管道不为空的情况下不消耗任何数据?
注意:如果管道还没有被写入或关闭,我 * 不 * 希望它阻塞。
3条答案
按热度按时间ut6juiuv1#
如果您使用Unix域流套接字而不是管道(这意味着您将
pipe(fds)
调用替换为socketpair(AF_UNIX, SOCK_STREAM, 0, fds)
),则可以使用recv(fd, dummybuffer, 1, MSG_PEEK)
读取/接收一个字节的数据,而无需将其从接收缓冲区中删除。如果不想阻塞,可以将
MSG_PEEK
与MSG_DONTWAIT
组合使用;如果想阻塞,可以将MSG_PEEK
与MSG_WAITALL
组合使用,直到填满整个缓冲区。Unix域流套接字和管道之间的差别很小。流套接字是双向的,但是可以使用
shutdown(fd, SHUT_WR)
(或SHUT_RD
)关闭“写入结束”(对应于“read end”),这意味着如果另一端尝试从套接字读取,它们将立即获得流结束(read()
、recv()
等返回0)。(关闭“读取端”意味着当另一端尝试写入套接字时,它们将获得EPIPE
。)现在,我甚至想不出为什么一个使用管道的程序不能使用Unix域流套接字对。
如果您使用命名管道,则需要将
mkfifo()
和open()
更改为socket(AF_UNIX, SOCK_STREAM, 0)
,后跟bind()
以更改套接字地址。read()
、write()
甚至更高级别的标准I/O工具在Unix域流套接字上也能很好地工作(使用fdopen()
将套接字描述符转换为FILE
句柄)。如果您无法修改读取器,则可以创建一个最小的动态库,该动态库插入
openat()
(这是当前C库在fopen()
下使用的),对除套接字路径之外的所有路径调用原始openat()
,比如在环境变量中命名,并创建一个套接字并绑定到该套接字的套接字路径。您只需设置LD_PRELOAD
以指向这个插入库。换句话说,我确实相信从管道切换到Unix域流套接字没有真实的的障碍。
不能将
recv()
与管道一起使用,因为在Linux中,管道是使用特殊的文件系统而不是套接字来实现的。kiayqfof2#
不,没有办法做到你所描述的。确定你是否已经到达一个不可查找的文件(如管道)的末尾的方法是尝试从它读取。这不仅仅是自然的方法,而是 the 方法。
但是如果有任何可用的,
是的,我知道
而且(据我所知)无法将数据“放回”管道中。
这取决于。如果你用POSIX
read()
阅读,那么没有。如果你用FILE
Package 管道末端并使用stdio函数读取它,那么就有ungetc()
。尽管如此,这:
我想检查管道是否为空的程序部分离我处理数据的地方很远
看起来像是设计问题。在你真正 * 做 * 得到数据或看到EOF之前,你无法知道你是否会得到数据。这个过程(es)在管道的写入端执行任何操作之前,都可以延迟任意的时间量,即使该进程是由您提供的,您也无法完全控制其行为的这一方面。因此,在准备使用数据之前尝试检查EOF没有多大意义,因为您不能指望在不阻塞情况下获得答案。
,所以我宁愿在第一次读取之前不必保存数据。
我想你一定不想在没有数据要处理的情况下执行某种重量级的初始化。好吧,但我看不出这有什么大不了的。无论如何,你都需要提供存储来读取数据。这样做有什么错:
重点不在于这是一个适合你的程序的结构,而是可以这样构造程序,使得从初始读取开始存储数据(如果有的话)是相当干净和自然的。
deyfvvtc3#
如果你的stdin到达了eof,它是一个非阻塞机制来确定你是否到达了EOF,而不需要任何更复杂的工具。