我有一个简单的C ++应用程序,它应该从POSIX命名管道中读取行:
#include<iostream>
#include<string>
#include<fstream>
int main() {
std::ifstream pipe;
pipe.open("in");
std::string line;
while (true) {
std::getline(pipe, line);
if (pipe.eof()) {
break;
}
std::cout << line << std::endl;
}
}
脚步:
mkfifo in
。g++ -std=c++11 test.cpp && ./a.out
编译并运行C ++代码。in
管道:sleep infinity > in & # keep pipe open, avoid EOF
echo hey > in
echo cats > in
echo foo > in
kill %1 # this closes the pipe, C++ app stops on EOF
在Linux下执行此操作时,应用程序会在每个echo
命令之后成功显示输出(g ++ 8.2.1)。
在macOS上尝试整个过程时,输出仅在关闭管道后显示(即在kill %1
之后)。我开始怀疑某种缓冲问题,所以我试过禁用它:
std::ifstream pipe;
pipe.rdbuf()->pubsetbuf(0, 0);
pipe.open("out");
通过此更改,应用程序在第一个echo
之后输出任何内容,然后在第二个echo
(“嘿”)之后输出第一条消息,并且继续这样做,alwasy滞后于后面的消息并显示先前echo
的消息而不是一个被执行。最后一条消息仅在关闭管道后显示。
我发现在macOS上g++
基本上是clang++
,因为g++ --version
产生:“Apple LLVM版本10.0.1(clang-1001.0.46.3)”。在使用Homebrew安装真正的g ++之后,示例程序就像在Linux上一样工作。
我正在构建一个基于命名管道构建的简单IPC库,原因有多种,所以此时此工作正常对我来说非常重要。
使用LLVM时导致这种奇怪行为的原因是什么? (更新:这是由libc ++引起的)
这是一个错误吗?
这种方式是否适用于C ++标准以某种方式保证的g ++?
如何使用clang++
使这段代码段正常工作?
更新:
这似乎是由getline()
的libc ++实现引起的。相关链接:
问题仍然存在。
正如单独讨论的那样,boost::asio
解决方案是最好的,但你的问题具体是关于getline
是如何阻止的,所以我将与之对话。
这里的问题是std::ifstream
并不是真正的FIFO文件类型。在getline()
的情况下,它正在尝试进行缓冲读取,因此(在最初的情况下)它决定缓冲区没有足够的数据到达分隔符('\n'
),在底层的underflow()
上调用streambuf
,这样做简单读取缓冲区长度的数据。这适用于文件,因为文件在某个时间点的长度是可知的长度,因此如果没有足够的数据来填充缓冲区,它可以返回EOF,如果有足够的数据,它只返回填充的缓冲区。但是,使用FIFO时,耗尽数据并不一定意味着EOF
,因此在写入它的进程关闭之前它不会返回(这是你的无限sleep
命令将其保持打开状态)。
更典型的方法是让编写器在读取和写入时打开和关闭文件。当poll()
/ epoll()
这样功能更强大的东西可用时,这显然是浪费精力,但我正在回答你问的问题。