使用boost::process管道链时无法关闭stdin致进程无法终止
问题描述
我有多个C语言工具,每个工具从stdin读取数据、处理后将结果写入stdout,当stdin关闭时停止运行。简化的子进程代码如下:
child.c
#include <stdio.h> int main() { char buf[256]; int nread; do { nread = fread(buf, 1, 256, stdin); // 数据处理逻辑... fwrite(buf, 1, 256, stdout); } while (nread > 0); return 0; }
通过bash管道可以将这些工具链式调用,例如:
u1 < data.bin | u2 | u3 | ... | un > result.bin
当第一个进程的stdin读到文件末尾并关闭后,链中所有进程会依次终止。
但用C++的boost::process实现复杂工具链时遇到问题:单个子进程关闭stdin后能正常停止,但用boost::process::pstream或boost::process::pipe连接两个及以上子进程时,关闭第一个子进程的stdin后无反应,第一个进程仍处于等待状态。
正常示例:单个子进程
#include <boost/process.hpp> namespace bp = boost::process; int main(int argc, char *argv[]) { bp::opstream cstdin; bp::ipstream cstdout; bp::child c0("child", bp::std_in<cstdin, bp::std_out> cstdout); cstdin.pipe().close(); cstdin.close(); c0.wait(); return 0; }
异常示例:两个子进程
#include <boost/process.hpp> namespace bp = boost::process; int main(int argc, char *argv[]) { bp::opstream cstdin; bp::ipstream cstdout; bp::pstream connector; bp::child c0("child", bp::std_in<cstdin, bp::std_out> connector); bp::child c1("child", bp::std_in<connector, bp::std_out> cstdout); cstdin.pipe().close(); cstdin.close(); c0.wait(); c1.wait(); return 0; }
运行环境:Debian 10、gcc 8.3、boost 1.80。
解决方案
1. 修正子进程代码(推荐)
子进程中当前固定写入256字节,即使fread返回0(已读到EOF)也会写入空数据,这会导致后续管道阻塞。修改为只写入实际读取的字节数:
// 替换原fwrite语句 fwrite(buf, 1, nread, stdout);
这样当stdin关闭时,nread=0,子进程不会写入任何数据,能直接退出,后续进程也会因上游管道关闭而依次终止。
2. 父进程主动读取输出避免阻塞
如果无法修改子进程代码,父进程需要持续读取最后一个子进程的stdout内容,防止下游进程因管道满而阻塞,进而导致上游进程卡住:
#include <boost/process.hpp> #include <iostream> namespace bp = boost::process; int main(int argc, char *argv[]) { bp::opstream cstdin; bp::ipstream cstdout; bp::pipe connector; // 改用单向pipe替代pstream bp::child c0("child", bp::std_in<cstdin, bp::std_out> connector); bp::child c1("child", bp::std_in<connector, bp::std_out> cstdout); // 关闭父进程持有的connector两端中不需要的部分 connector.close(); cstdin.pipe().close(); cstdin.close(); // 读取cstdout的内容,避免c1阻塞 std::string line; while (std::getline(cstdout, line)); c0.wait(); c1.wait(); return 0; }
另外,使用bp::pipe替代bp::pstream作为进程间的单向连接更合适,同时父进程要关闭不需要的pipe端点,确保EOF能正确传递给子进程。
内容的提问来源于stack exchange,提问作者AtremSh
相关产品推荐
相关产品推荐

