如何重现Boost流程文档暗示的死锁?

Ton*_*vel 6 c++ boost boost-process

根据Boost文档("管道为什么不关闭?"部分),以下代码将导致死锁:

#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;
}
Run Code Online (Sandbox Code Playgroud)

文件说:

这也会死锁,因为子进程退出时管道不会关闭.因此,即使进程已经结束,ipstream仍将查找数据.

但是,我无法重现死锁(在Linux下).此外,我不明白为什么首先会发生僵局.子进程退出后,它会关闭管道的写入端.管道的读取端仍然可供父进程读取,并且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;
}
Run Code Online (Sandbox Code Playgroud)

我想知道这是否真的是Linux下进程产生的一些不可避免的属性.使用Facebook的Folly库中的Subprocess重现上面的例子至少不会死锁:

#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;
}
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 3

\n

一旦子进程退出,它就会关闭管道的写端。

\n
\n\n

这似乎是假设。什么程序关闭什么管道?

\n\n

如果/bin/ls是的话,会发生什么

\n\n
bp::child c("/bin/bash", bp::args({"-c", "ls; ls"}));\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果ls真的关闭的话,应该关闭两次。

\n\n

也许 bash 在引擎盖下复制了句柄,因此子进程关闭不同的同一管道的我不确定这些语义的可靠性\xc2\xb9

\n\n

因此,显然 stdout 得到了很好的满足。但是,在 Linux 上使用非标准文件描述符进行输出时,我可以重现死锁:

\n\n
#include <boost/process.hpp>\n#include <iostream>\n\nnamespace bp = ::boost::process;\n\nint main() {\n    bp::ipstream is;\n    bp::child c("/bin/bash", bp::args({"-c", "exec >&40; ls"}), bp::posix::fd.bind(40, is.rdbuf()->pipe().native_sink()));\n\n    std::string line;\n    while (std::getline(is, line)) {\n        std::cout << line << "\\n";\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不确定为什么 bash 中的子进程的“关闭标准输出”行为在重定向到 fd 时应该表现不同,但就是这样。

\n\n

演示相关死锁的另一种好方法是:

\n\n
{\n    bp::child c("/bin/bash", bp::args({"-c", "ls -R /"}), bp::std_out > is);\n    c.wait();\n    return c.exit_code();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个答案不是结论性的,但确实观察到了一些要点并在 Linux 上进行了演示:

\n\n
    \n
  • 并非所有文件描述符似乎都与标准输出相同
  • \n
  • 死锁可能发生在子进程异步处理 IO 但调用进程同步处理它们的许多场景中。
  • \n
\n\n

我认为后者是文档中的重点。

\n\n
\n\n

\xc2\xb9 实际上,文档明确表明这些语义的差异是 Win32 中的问题:

\n\n
\n

在此库中不可能使用自动管道关闭,因为管道可能是文件句柄(对于 Windows 上的异步管道)

\n
\n