You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Boost async_pipe跨fork传递子进程输出时read_loop无法终止的问题

问题

刚接触boost::asio和boost::process库,遇到了一个难以解决的问题:
编写的测试程序流程如下:

  • 调用fork()分为父进程和子进程分支;
  • 子进程分支使用boost::process::child类在异步环境中执行Unix命令ls;
  • 子进程将boost::process::async_pipe传入该类,将std_out输出到管道;
  • 父进程分支希望逐行读取管道内容并进一步处理。

目前实现能正常输出内容,但父进程中的read_loop()调用无法终止,仿佛永远没到达EOF或被阻塞。以下是最小可复现示例(MWE):

#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>

#include <unistd.h>

void read_loop(boost::process::async_pipe& pipe)
{
    static boost::asio::streambuf buffer;
    boost::asio::async_read_until(
        pipe,
        buffer,
        '\n',
        [&](boost::system::error_code error_code, std::size_t bytes) {
            if (!error_code) {
                std::istream is(&buffer);
                if (std::string line; std::getline(is, line)) {
                    std::cout << "Read Line: " << line << "\n";
                }
                read_loop(pipe);
            }
            else {
                std::cout << "Error in read_loop()!\n";
                    pipe.close();
            }
        }
    );
}

int main(int argc, char* argv[])
{
    boost::asio::io_context io_context{};
    boost::process::async_pipe pipe{ io_context };

    io_context.notify_fork(boost::asio::io_context::fork_prepare);
    pid_t pid{ fork() };

    if (pid == 0) {
        io_context.notify_fork(boost::asio::io_context::fork_child);
        boost::process::child child(
            boost::process::args({ "/usr/bin/ls", "/etc/" }),
            boost::process::std_out > pipe,
            boost::process::on_exit([&](int exit, std::error_code error_code) { std::cout << "[Exited with code " << exit << " (" << error_code.message() << ")]\n"; }),
            io_context
        );
        io_context.run();
    }
    else {
        io_context.notify_fork(boost::asio::io_context::fork_parent);
        read_loop(pipe);
        io_context.run();
    }

    return 0;
}

程序能输出预期内容,但最后会挂起直到被强制终止。请问为何read_loop()函数会阻塞/无法正常退出?


原因分析

核心问题在于管道写端的引用计数未清零:

  • fork()会复制所有文件描述符,因此父进程和子进程各自持有async_pipe的写端句柄;
  • 子进程中ls命令执行完毕后,boost::process::child会关闭自身持有的写端,但父进程的写端副本仍然处于打开状态;
  • 管道的读端只有在所有写端都关闭时,才会触发EOF(boost::asio::error::eof)信号;
  • 父进程的read_loop()一直在等待数据,永远收不到EOF,导致io_context.run()无法退出,程序挂起。

此外代码还有两处细节问题:

  • read_loop中的static streambuf属于全局状态,会引发意外的内存残留问题;
  • 错误处理分支未区分EOF和真正的错误,将正常结束信号误判为错误。

解决方案

1. 父进程主动关闭管道写端

在父进程分支中,明确关闭async_pipe的写端,确保子进程写端关闭后,管道没有剩余写端:

else {
    io_context.notify_fork(boost::asio::io_context::fork_parent);
    // 关闭父进程持有的管道写端
    pipe.close(boost::asio::write_side);
    read_loop(pipe);
    io_context.run();
}

2. 修正read_loop的错误逻辑和静态buffer

将静态streambuf改为局部共享对象,同时区分EOF和错误场景:

void read_loop(boost::process::async_pipe& pipe)
{
    auto buffer = std::make_shared<boost::asio::streambuf>();
    boost::asio::async_read_until(
        pipe,
        *buffer,
        '\n',
        [&, buffer](boost::system::error_code error_code, std::size_t bytes) {
            if (!error_code) {
                std::istream is(buffer.get());
                std::string line;
                if (std::getline(is, line)) {
                    std::cout << "Read Line: " << line << "\n";
                }
                read_loop(pipe);
            }
            else if (error_code == boost::asio::error::eof) {
                std::cout << "Read completed (EOF)\n";
                pipe.close();
            }
            else {
                std::cout << "Error in read_loop(): " << error_code.message() << "\n";
                pipe.close();
            }
        }
    );
}

3. 可选:子进程关闭管道读端

子进程不需要读取管道,可主动关闭读端减少资源占用:

if (pid == 0) {
    io_context.notify_fork(boost::asio::io_context::fork_child);
    pipe.close(boost::asio::read_side); // 关闭子进程的读端
    boost::process::child child(
        boost::process::args({ "/usr/bin/ls", "/etc/" }),
        boost::process::std_out > pipe,
        boost::process::on_exit([&](int exit, std::error_code error_code) { std::cout << "[Exited with code " << exit << " (" << error_code.message() << ")]\n"; }),
        io_context
    );
    io_context.run();
}

内容的提问来源于stack exchange,提问作者Matryka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 20:15:42