简体   繁体   English

使用boost.process同时读取和写入child的stdio

[英]simultaneous read and write to child's stdio using boost.process

i am trying to write and read to child's stdio using boost.process using something like this: 我正在尝试使用boost.process使用类似这样的东西来编写和读取孩子的stdio:

boost::asio::io_service writeService, readService;
bp::async_pipe in{writeService};
bp::async_pipe out{readService};

bp::child process(CompressCmd.c_str(), bp::std_in < in, bp::std_out > out);
Buffer src;
src.reserve(4 * 1024 * 1024);
integer_type read = 0;
//std::atomic_int64_t totalWrite{0};
integer_type totalWrite = 0;
while (callback(CallbackActions::NeedMoreInput, src, read)) {
    in.async_write_some(
        boost::asio::buffer(src.data(), read),
        [](const boost::system::error_code &e, std::size_t) { });
    // written data is not important, that's why using same buffer
    out.async_read_some(boost::asio::buffer(src.data(), src.capacity()),
                        [&](const boost::system::error_code &e,
                           std::size_t byte_transferred) { totalWrite += byte_transferred; });
}
writeService.run();
in.close();
readService.run();

all read and write operations are noitified as success but value of totalWrite is totally incorrect fe reported 29356032, real value should be around 50000000 所有的读写操作都被认定为成功但是totalWrite的值完全不正确fe报告29356032,实际值应该在50000000左右
i noticed the program is terminating half way , 我注意到程序正在中途终止,
using process.wait() after readService.run() freezes child, 在readService.run()冻结child后使用process.wait(),
using atomic int produces same behaviour 使用atomic int会产生相同的行为

for now i actually only need to know how much data is actually wriiten, that's why i am using same buffer 现在我实际上只需要知道实际上有多少数据,这就是为什么我使用相同的缓冲区

  1. This pattern: 这种模式:

     while (callback(CallbackActions::NeedMoreInput, src, read)) { in.async_write_some(...); out.async_read_some(...); } 

    is most likely misguided (async operations always immediately return, so you'd simply keep adding more async operations without giving them a chance to run). 很可能被误导(异步操作总是立即返回,因此您只需继续添加更多异步操作而不给他们机会运行)。

  2. Also misguided is the fact that you have separate services for the pipes, but you're running them in total exclusion, so no read operation will ever run until the writeService completes. 同样误导的是,您为管道提供了单独的服务,但是您在完全排除的情况下运行它们,因此在writeService完成之前不会运行任何读取操作。

  3. atomic types are misguided since there's no access from multiple threads atomic类型被误导,因为没有来自多个线程的访问权限

  4. What are you trying to do? 你想做什么? You reserve a large buffer but never put any data into it ( reserve != resize ). 您保留一个大缓冲区,但从不将任何数据放入其中( reserve != resize )。 Therefore you can only hope to write nothing. 因此,你只能希望什么都不写。

    Even more ironically, you are reading into the exact same buffer, at the exact same spot. 更具讽刺意味的是,你正在完全相同的位置读取完全相同的缓冲区。 However, that's immediately Undefined Behaviour ¹ because you pass it src.capacity() when you know that src.size()==0 . 然而,这立即未定义行为 ¹,因为你通过它src.capacity()当你知道src.size()==0

    Even without that error how can you "simultaneously" read and write from exactly the same bytes in memory and still know what the expected outcome would be? 即使没有那个错误,你如何“同时”从内存中完全相同的字节读取和写入,仍然知道预期的结果是什么?

  5. you are not passing your own io_service to Boost Process 您没有将自己的io_service传递给Boost Process

A Working Demo 一个工作演示

Here's a working sample. 这是一个工作样本。 Of course I had to guess what you actually want to do. 当然,我不得不猜测你真正想做什么。

I opted to make the program send its own source (main.cpp) to stdin, and read stdout iteratively, recording the total_received bytes. 我选择让程序将自己的源(main.cpp)发送到stdin,并迭代读取stdout,记录total_received字节。 It then prints the exit code and that total. 然后打印退出代码和总数。

As a make-shift compressor I used '/usr/bin/xxd' because it's available and could even be usefully printed to std::cout for debugging. 作为一个make-shift压缩器,我使用'/usr/bin/xxd'因为它可用,甚至可以有效地打印到std::cout进行调试。

Live On Coliru // trouble on Coliru 住在Coliru // 在Coliru遇到麻烦

#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
std::vector<char> read_file(std::string const&);

namespace bp = boost::process;
using boost::system::error_code;

using Loop = boost::function<void()>;
using Buffer = std::array<char, 4*1024>;

int main() {
    boost::asio::io_service svc;

    std::string const CompressCmd = "/usr/bin/xxd";

    bp::async_pipe in{svc}, out{svc};
    bp::child process(CompressCmd, bp::std_in < in, bp::std_out > out, svc);

    auto data = read_file("main.cpp");

    Loop read_loop, write_loop;

    Buffer recv_buffer;
    std::size_t total_received = 0;
    read_loop = [&read_loop, &out, &recv_buffer, &total_received] {
        out.async_read_some(boost::asio::buffer(recv_buffer),
            [&](error_code ec, size_t transferred) {
                std::cout << "ReadLoop: " << ec.message() << " got " << transferred << " bytes\n";
                total_received += transferred; 
                if (!ec)
                    read_loop(); // continue reading
            });
    };

    boost::asio::async_write(in, boost::asio::buffer(data),
        [&](error_code ec, size_t transferred) {
            std::cout << "WriteLoop: " << ec.message() << " done, " << transferred << " bytes\n";
            in.close(ec);
            std::cout << "WriteLoop: closed pipe (" << ec.message() << ")\n";
        }); // async

    read_loop(); // async

    svc.run(); // Await all async operations

    std::cout << "Process exitcode " << process.exit_code() << ", total_received=" << total_received << "\n";
}

#include <fstream>
#include <iterator>
std::vector<char> read_file(std::string const& fname) {
    std::ifstream ifs(fname);
    return {std::istreambuf_iterator<char>(ifs), {}};
}

Printing 印花

WriteLoop: Success done, 1787 bytes
WriteLoop: closed pipe (Success)
ReadLoop: Success got 4096 bytes
ReadLoop: Success got 3515 bytes
ReadLoop: End of file got 0 bytes
Process exitcode 0, total_received=7611

Explanations, Simplifications 解释,简化

Note that we do all of the writing without a loop. 请注意,我们在没有循环的情况下完成所有写操作。 That's because boost::asio::async_write is a composed operation (it hides the loop). 那是因为boost::asio::async_write是一个组合操作 (它隐藏了循环)。

Likewise, if you can "afford" to store the whole received data in memory, you can simplify by using boost::asio::streambuf and using a similar composed operation: 同样,如果你能“负担”将整个接收的数据存储在内存中,你可以通过使用boost::asio::streambuf并使用类似的组合操作来简化:

Live On Coliru // trouble on Coliru 住在Coliru // 在Coliru遇到麻烦

#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
std::vector<char> read_file(std::string const&);

namespace bp = boost::process;
using boost::system::error_code;

int main() {
    boost::asio::io_service svc;

    std::string const CompressCmd = "/usr/bin/xxd";

    bp::async_pipe in{svc}, out{svc};
    bp::child process(CompressCmd, bp::std_in < in, bp::std_out > out, svc);

    auto data = read_file("main.cpp");

    boost::asio::streambuf recv_buffer;
    boost::asio::async_read(out, recv_buffer,
            [&](error_code ec, size_t transferred) {
                std::cout << "ReadLoop: " << ec.message() << " got " << transferred << " bytes\n";
            });

    boost::asio::async_write(in, boost::asio::buffer(data),
        [&](error_code ec, size_t transferred) {
            std::cout << "WriteLoop: " << ec.message() << " done, " << transferred << " bytes\n";
            in.close(ec);
            std::cout << "WriteLoop: closed pipe (" << ec.message() << ")\n";
        }); // async

    svc.run(); // Await all async operations

    std::cout << "Process exitcode " << process.exit_code() << ", total_received=" << recv_buffer.size() << "\n";
}

#include <fstream>
#include <iterator>
std::vector<char> read_file(std::string const& fname) {
    std::ifstream ifs(fname);
    return {std::istreambuf_iterator<char>(ifs), {}};
}

Conversely, if you cannot afford to have all the data in memory before sending, you can create a loop to send input block-wise 相反,如果你在发送之前无法承担内存中的所有数据,你可以创建一个循环来逐块发送输入

Two Asynchronous Loops, With Delays 两个异步循环,带延迟

Let's do that, and make it more entertaining by delaying a second before writing each block. 让我们这样做,并通过在编写每个块之前延迟一秒来使其更有趣。 What you'd expect to see is alternating reads/writes happening because of the delays: 您期望看到的是由于延迟而发生的交替读/写:

Live On Coliru // yay running on Coliru Live On Coliru // @ yay在Coliru上跑

#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
#include <fstream>

namespace bp = boost::process;
using boost::system::error_code;
using namespace std::chrono_literals;

using Loop = boost::function<void()>;
using Buffer = std::array<char, 500>;

int main() {
    boost::asio::io_service svc;
    auto on_exit = [](int code, std::error_code ec) {
            std::cout << "Exited " << code << " (" << ec.message() << ")\n";
        };

    std::string const CompressCmd = "/usr/bin/xxd";

    bp::async_pipe in{svc}, out{svc};
    bp::child process(CompressCmd, bp::std_in < in, bp::std_out > out, svc, bp::on_exit(on_exit));

    Loop read_loop, write_loop;

    Buffer recv_buffer;
    std::size_t total_received = 0;
    read_loop = [&read_loop, &out, &recv_buffer, &total_received] {
        out.async_read_some(boost::asio::buffer(recv_buffer),
            [&](error_code ec, size_t transferred) {
                std::cout << "ReadLoop: " << ec.message() << " got " << transferred << " bytes\n";
                total_received += transferred; 
                if (!ec)
                    read_loop(); // continue reading
            });
    };

    std::ifstream ifs("main.cpp");
    std::size_t total_written = 0;
    Buffer send_buffer;
    boost::asio::high_resolution_timer send_delay(svc);
    write_loop = [&write_loop, &in, &ifs, &send_buffer, &total_written, &send_delay] {
        if (!ifs.good())
        {
            error_code ec;
            in.close(ec);
            std::cout << "WriteLoop: closed stdin (" << ec.message() << ")\n";
            return;
        }
        ifs.read(send_buffer.data(), send_buffer.size());

        boost::asio::async_write(in, boost::asio::buffer(send_buffer.data(), ifs.gcount()),
            [&](error_code ec, size_t transferred) {
                std::cout << "WriteLoop: " << ec.message() << " sent " << transferred << " bytes\n";
                total_written += transferred; 
                if (!ec) {
                    send_delay.expires_from_now(1s);
                    send_delay.async_wait([&write_loop](error_code ec) {
                        std::cout << "WriteLoop: send delay " << ec.message() << "\n";
                        if (!ec) write_loop(); // continue writing
                    });
                }
            });
    };

    read_loop(); // async
    write_loop(); // async

    svc.run(); // Await all async operations

    std::cout << "Process exitcode " << process.exit_code() << ", total_received=" << total_received << "\n";
}

Prints 打印

WriteLoop: Success sent 500 bytes
WriteLoop: send delay Success
WriteLoop: Success sent 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 96 bytes
WriteLoop: send delay Success
WriteLoop: Success sent 500 bytes
WriteLoop: send delay Success
WriteLoop: Success sent 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 96 bytes
WriteLoop: send delay Success
WriteLoop: Success sent 500 bytes
WriteLoop: send delay Success
WriteLoop: Success sent 134 bytes
WriteLoop: send delay Success
WriteLoop: closed stdin (Success)
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 500 bytes
ReadLoop: Success got 22 bytes
Exited 0 (Success)
ReadLoop: End of file got 0 bytes
Process exitcode 0, total_received=11214

¹ perhaps just unspecified , I'm not inclined to find out the difference right now ¹或许只是未说明 ,我现在不想发现差异

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM