如何重现Boost过程文档提示的死锁? [英] How to reproduce deadlock hinted to by Boost process documentation?

查看:88
本文介绍了如何重现Boost过程文档提示的死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

According to the Boost documentation (section 'Why does the pipe not close?'), the following code will result in a deadlock:

#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仍将查找数据.

This will also deadlock, because the pipe does not close when the subprocess exits. So the ipstream will still look for data even though the process has ended.

但是,我无法重现死锁(在Linux下).此外,我不明白为什么首先会发生死锁.子进程退出后,它将关闭管道的 write-end .管道的读取端仍可用于父进程读取,并且一旦管道缓冲区中没有更多数据,并且 std :: getline()将失败.端被关闭,对吗?如果在子进程执行期间管道缓冲区已满,则子进程将阻止等待父进程从管道读取足够的数据,以便继续执行.

However, I am not able to reproduce the deadlock (under Linux). Furthermore, I do not understand why the deadlock would occur in the first place. Once the child process exits it closes the write-end of the pipe. The read-end of the pipe will still be available for the parent process to read from, and std::getline() will fail once no more data is available in the pipe buffer, and the write-end was closed, correct? In case the pipe buffer fills up during execution of the child process, the child process will block waiting for the parent process to read enough data from the pipe so that it can continue.

因此,如果上述代码可能发生死锁,是否有一种简单的方法可以重现死锁情况?

So in case the above code can deadlock, is there an easy way to reproduce the deadlock scenario?

更新:

实际上,以下代码使用Boost进程死锁:

Indeed, the following piece of code deadlocks using Boost process:

#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的愚蠢库至少不会死锁:

I wonder whether this really is some unavoidable property of process spawning under Linux though. Reproducing the above example using Subprocess from Facebook's Folly library at least does not deadlock:

#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;
}

推荐答案

一旦子进程退出,它将关闭管道的写端.

Once the child process exits it closes the write-end of the pipe.

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

This seems to be the assumption. What program closes what pipe?

如果/bin/ls 这样做了,会发生什么情况

If /bin/ls does, what happens for

bp::child c("/bin/bash", bp::args({"-c", "ls; ls"}));

如果 ls 确实确实将其关闭,则应将其关闭两次.

If ls really does close it, then it should be closed twice.

也许bash在后台复制了手柄,所以子进程关闭同一管道的不同副本.我不确定这些语义的可靠性¹

Perhaps bash duplicates the handles under the hood, so the subprocesses close different copies of the same pipe. I'm not sure about the reliability of these semantics¹

因此,显然stdout值得推荐.但是,当在Linux上使用非标准文件描述符输出时,我可以重现死锁:

So, apparently stdout is well-catered for. However, I can reproduce the deadlock when using a non-standard file-descriptor for output on 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中的子进程的关闭标准输出"行为重定向到fd时行为会有所不同,但是您去了.

I'm not sure why the "closing stdout" behaviour of sub processes in bash should behave differently when it was redirected to an fd, but there you go.

另一个证明相关死锁的好方法是:

Another nice way to demonstrate a related deadlock is:

{
    bp::child c("/bin/bash", bp::args({"-c", "ls -R /"}), bp::std_out > is);
    c.wait();
    return c.exit_code();
}

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

This answer is not conclusive but does observe some points and demonstrate them on linux:

  • 并非所有文件描述符都与stdout一样覆盖
  • 在许多情况下会发生死锁,其中子流程异步处理其IO,而调用流程则同步处理它们.

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

I think the latter was the point in the documentation.

¹实际上,文档明确暗示了这些语义上的差异是Win32中的问题:

¹ indeed the documentation explicitly suggests difference in these semantics are the problem in Win32:

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

这篇关于如何重现Boost过程文档提示的死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆