根据Boost documentation(部分'为什么管道未关闭?'),以下代码将导致死锁:
#include <boost/process.hpp>
#include <iostream>
namespace bp = ::boost::process;
int main(void)
{
bp::ipstream is;
bp::child c("ls", bp::std_out > is);
std::string line;
while (std::getline(is, line))
{
std::cout << line << "\n";
}
return 0;
}
文档说:
这也会死锁,因为子进程退出时管道不会关闭。因此,即使进程已经结束,ipstream仍会查找数据。
但是,我无法重现死锁(在Linux下)。此外,我不明白为什么首先会发生僵局。子进程退出后,它将关闭管道的 write-end 。管道的读取端仍然可供父进程读取,并且一旦管道缓冲区中没有更多数据可用,std::getline()
将失败,并且写入端已关闭,正确吗?如果在执行子进程期间管道缓冲区填满,子进程将阻止等待父进程从管道读取足够的数据,以便它可以继续。
所以如果上面的代码可以死锁,有没有一种简单的方法来重现死锁场景?
更新
确实,下面的一段代码使用Boost进程死锁:
#include <boost/process.hpp>
#include <iostream>
namespace bp = ::boost::process;
int main()
{
bp::ipstream is;
bp::child c("/bin/bash", bp::args({"-c", "ls >&40"}), bp::posix::fd.bind(40, is.rdbuf()->pipe().native_sink()));
std::string line;
while (std::getline(is, line))
{
std::cout << line << "\n";
}
c.wait();
return 0;
}
我想知道这是否真的是Linux下进程产生的一些不可避免的属性。使用Facebook Subprocess库中的Folly重现上述示例至少不会陷入僵局:
#include <folly/Subprocess.h>
#include <iostream>
int main()
{
std::vector<std::string> arguments = {"/bin/bash", "-c", "ls >&40"};
folly::Subprocess::Options options;
options.fd(40, STDOUT_FILENO);
folly::Subprocess p(arguments, options);
std::cout << p.communicate().first;
p.wait();
return 0;
}
答案 0 :(得分:2)
子进程退出后,它将关闭管道的写入端。
这似乎是假设。什么程序关闭什么管道?
如果/bin/ls
有,
bp::child c("/bin/bash", bp::args({"-c", "ls; ls"}));
如果ls
确实关闭它,那么它应该关闭两次。
也许bash会复制引擎盖下的句柄,因此子进程会关闭同一管道的不同副本。我不确定这些语义的可靠性¹
所以,显然stdout很适合。但是,当我在linux上使用非标准文件描述符输出时,我可以重现死锁:
#include <boost/process.hpp>
#include <iostream>
namespace bp = ::boost::process;
int main() {
bp::ipstream is;
bp::child c("/bin/bash", bp::args({"-c", "exec >&40; ls"}), bp::posix::fd.bind(40, is.rdbuf()->pipe().native_sink()));
std::string line;
while (std::getline(is, line)) {
std::cout << line << "\n";
}
}
我不确定为什么bash中子进程的“关闭stdout”行为在被重定向到fd时应该有不同的行为,但是你去了。
另一种证明相关死锁的好方法是:
{
bp::child c("/bin/bash", bp::args({"-c", "ls -R /"}), bp::std_out > is);
c.wait();
return c.exit_code();
}
这个答案并不是决定性的,但确实观察了一些观点并在linux上进行了演示:
我认为后者是文档中的重点。
¹确实文档明确表明这些语义的差异是Win32中的问题:
在此库中无法自动使用管道关闭,因为管道可能是文件句柄(对于Windows上的异步管道)