启动子进程时的争用条件导致从管道读取挂起
Race condition in starting up sub processes causes reading from pipe to hang
我有两个线程,每个线程启动一个子进程。第一个应用程序是一个运行相当长的二进制文件。第二个退出得很快。
存在有时会导致此操作失败的争用条件。下面我有一个最小可行的代码示例。
它使用Boost Process 0.5,它使用标准的fork/execve/dup2系统。关于提升过程的工作原理有一些技巧,但总的来说它运行良好。
父进程启动了更多的进程,并且通常它可以工作。
我不能一次启动一个进程,特别是因为我不知道哪些部分不能交错。
关于为什么会挂起的任何想法?
预期产出:
/etc/init.d/led restart: Creating child
Creating child1
Reading STDOUT
/etc/init.d/led restart: Waiting for it to exit
Reading std_err_pipe
wait_for_exit(pullapp);
Reading std_out_pipe
< file list>
Done
然而,通常,但并非总是,它止步于std_err_pipe。
#include <iostream>
#include <string>
#include <vector>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>
#include <boost/thread.hpp>
void run_sleep()
{
int exit_code;
std::string str;
std::vector< std::string > args;
boost::shared_ptr<boost::process::child> child;
args.push_back(boost::process::search_path("sleep"));
args.push_back("20");
boost::iostreams::stream< boost::iostreams::file_descriptor_source >
out_stream;
boost::process::pipe out_pipe = boost::process::create_pipe();
{
//MUST BE IN SEPARATE SCOPE SO SINK AND SOURCE ARE DESTROYED
// See http://stackoverflow.com/a/12469478/5151127
boost::iostreams::file_descriptor_sink out_sink
(out_pipe.sink, boost::iostreams::close_handle);
boost::iostreams::file_descriptor_source out_source
(out_pipe.source, boost::iostreams::close_handle);
std::cout << "Creating child1" << std::endl;
child.reset(new boost::process::child(
boost::process::execute(
boost::process::initializers::run_exe(args[0]),
boost::process::initializers::set_args(args),
boost::process::initializers::bind_stdout(out_sink),
boost::process::initializers::bind_stderr(out_sink)
)
));
out_stream.open(out_source);
}
std::cout << "Reading STDOUT" << std::endl;
while( out_stream ) {
std::string line;
std::getline(out_stream, line);
std::cout << line << std::endl;
}
std::cout << "wait_for_exit(pullapp);" << std::endl;
exit_code = wait_for_exit(*child);
child.reset();
return;
}
void run_ls()
{
int exit_code;
std::string str;
std::vector< std::string > args ;
args.push_back(boost::process::search_path("ls"));
args.push_back("/lib");
boost::process::pipe std_out_pipe = boost::process::create_pipe();
boost::process::pipe std_err_pipe = boost::process::create_pipe();
std::cout << "/etc/init.d/led restart: Creating child" << std::endl;
{
boost::process::child child = boost::process::execute(
boost::process::initializers::set_args(args),
boost::process::initializers::bind_stdout(
boost::iostreams::file_descriptor_sink(
std_out_pipe.sink,
boost::iostreams::close_handle
)
),
boost::process::initializers::bind_stderr(
boost::iostreams::file_descriptor_sink(
std_err_pipe.sink,
boost::iostreams::close_handle
)
),
boost::process::initializers::throw_on_error()
);
std::cout << "/etc/init.d/led restart: Waiting for it to exit" << std::endl;
exit_code = wait_for_exit(child);
}
{ //with std_err_stream, istream
boost::iostreams::stream< boost::iostreams::file_descriptor_source >
std_err_stream(
boost::iostreams::file_descriptor_source(
std_err_pipe.source, boost::iostreams::close_handle
)
);
std::cout << "Reading std_err_pipe" << std::endl;
std::istream istream(std_err_stream.rdbuf());
while( istream ) {
getline(istream, str);
std::cout << str << std::endl;
}
}
{ //with std_out_stream, istream
boost::iostreams::stream< boost::iostreams::file_descriptor_source >
std_out_stream(
boost::iostreams::file_descriptor_source(
std_out_pipe.source, boost::iostreams::close_handle
)
);
std::cout << "Reading std_out_pipe" << std::endl;
std::istream istream(std_out_stream.rdbuf());
while( istream ) {
getline(istream, str);
std::cout << str << std::endl;
}
}
std::cout << "Done" << std::endl;
}
int main()
{
boost::thread run_sleep_tr(run_sleep);
boost::thread run_ls_tr(run_ls);
run_sleep_tr.join();
run_ls_tr.join();
return 0;
}
(另存为进程测试.cpp并用g++ process-test.cpp -o process-test -lboost_iostreams -lboost_filesystem -lboost_thread -lboost_system
编译(
显然,
这是因为文件句柄最终出现在多个进程中。这些进程不会关闭这些句柄,因此父进程仍在等待。
对于Linux,修复相对容易;管道应该在create_pipe
中使用O_CLOEXEC
创建。bind_*
方法中的dup2
调用会清除此标志,这足以使管道正常工作。
在Windows上,我还没有真正找到解决方案。必须将句柄标记为可继承。可以在executor()
方法中执行此操作,但也许这需要全局互斥锁。我还没有时间好好研究一下。
我不确定"使用 boost.process 0.6"是否算作答案,但这对你来说就是这样。在几个错误报告之后。在窗户上关闭父亲过程中的水槽就足够了。
相关文章:
- 先进先出:一个进程永远不会从管道读取
- read() 在 select() 阻塞之后,当从生成的进程从管道读取时
- 读取文件挂在管道读取上
- 使用两个不同的管道C 读取和写入相同的过程
- 启动子进程时的争用条件导致从管道读取挂起
- 为什么从管道读取时libc++getline会阻塞,而libstdc++getline不会
- 如何从 GStreamer 管道读取数据中的特定字段
- 命名管道读取超时
- 使用“反引号”而不是管道读取值 |C++
- 从管道读取输入时出现scanf问题
- 从 Linux 中的多个非阻塞命名管道读取
- 父进程没有完全从命名管道读取数据
- 获取错误代码 998 尝试从命名管道读取时对内存位置的访问无效
- 无法从匿名管道读取
- 在dup2()之后使用std::cin从管道读取
- 为什么 ReadFile() 不返回 0 ?程序尝试永远从管道读取数据
- 执行控制台程序,写入标准输入并使用管道读取结果
- 从管道读取随机失败
- 为什么我的程序在管道读取过程中冻结?
- 为什么可以'd '从管道读取比我自己的程序使用ifstream更快