【发布时间】:2019-08-25 00:41:01
【问题描述】:
我有一个简单的 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(“嘿”)之后打印出第一条消息,并继续这样做,总是滞后一条消息并显示该消息之前的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++ 使这段代码 sn-p 正常工作?
更新:
这似乎是由 getline() 的 libc++ 实现引起的。
相关链接:
- Why does libc++ getline block when reading from pipe, but libstdc++ getline does not?
- https://bugs.llvm.org/show_bug.cgi?id=23078
问题仍然存在。
【问题讨论】:
-
语言标准没有说明任何输出的时间。 (它可能应该说一点,但不是关于这种多进程情况。)但是,POSIX 确实如此,而且这看起来确实有问题——如果你使用 DOS 换行符 (
echo $'foo\r' >in) 会发生什么?跨度> -
@DavisHerring 是的,你是对的,我可能应该更准确地撰写我的话。当然,我的意思是
std::getlines()如果流有可用的换行符,则不会阻塞。尝试使用 DOS 换行符产生了相同的结果。 -
奇怪的是,我看到较旧的“Apple LLVM 版本 7.0.2 (clang-700.1.81)”具有所需的行为,
_LIBCPP_VERSION为 1101。 -
在链接的错误报告中,我发现了以下响应:“问题实际上出在 basic_filebuf<_chart _traits>::underflow() 中。在第 595 行附近,它调用 fread 来填充 filebuf 的缓冲区. (默认情况下,4096 字节)。此读取挂起,直到它可以读取整个缓冲区已满。当它与文件交谈时,文件没有那么多数据,它会得到一个短读取。当它是与管道交谈时,它只是等到有那么多数据可用。”这似乎表明这确实是一个错误。当我有时间时,我可能应该检查我们尝试过的两个版本中的引用代码。
-
我很欣赏这个建议,因为它是一个很好的建议。事实上,我之前曾使用过 boost 的 asyncio,是的,这将是一般 IPC 的优雅解决方案。但是,问题不是“如何构建可扩展且灵活的 IPC 解决方案”。与大多数项目一样,我有一些超出我能力范围的需求。问题是关于
std::getline()在 libc++ 中的奇怪行为,而不是我从命名管道读取直到分隔符的原因。
标签: c++ macos pipe clang named-pipes