提升async_pipe不显示所有子过程输出

发布于 2025-01-23 00:21:19 字数 2937 浏览 4 评论 0原文

我遇到了一个障碍。下面的代码有问题,但这只是一个演示。我想首先使高级逻辑正确。

在到达“就绪”状态之前,这两个启动应用程序会输出大量启动信息。在此状态下,程序A可以通过STDIN为用户输入。程序B仅通过网络连接来聆听 - 最激烈的和记录数据。

理想情况下,使用此示例程序,我应该能够在“实时”中看到程序B的输出。但是在每个循环迭代中,什么都没有发生。我不确定它是否通过管道接收输入。

我以前在使用bp :: opstream写信给孩子 - program a-stdin。我知道是否通过程序A通过其Async_pipe接受了一些命令,Progam B节目还显示了一些记录信息(例如“ Trip”)。这些是窗口控制台应用程序,我正在使用Boost C ++作为子过程与它们进行交互。

有人有任何想法吗?



std::size_t read_loop(bp::async_pipe& p, mutable_buffer buf, boost::system::error_code &err)
{
    return p.read_some(buf, err);


}



void read_loop_async(bp::async_pipe& p, mutable_buffer buf, std::error_code &err) {
    p.async_read_some(buf, [&p, buf, &err](std::error_code ec, size_t n) {
        std::cout << "Received " << n << " bytes (" << ec.message() << "): '";
        std::cout.write(boost::asio::buffer_cast<char const*>(buf), n) << std::endl;
        err = ec;

        if (!ec)
            read_loop_async(p, buf, err);

    });
}


void write_pipe(bp::async_pipe&p, mutable_buffer buf)
{
    ba::async_write(p, buf, [](boost::system::error_code ec, std::size_t sz)
    {
        std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
    });

}


int main()
{

    bp::opstream sendToChild;
    string wd = "<---path-to-working-dir----->";
    ba::io_service ios;
    string bin = "<path-to-bin-and-name>";


    bp::async_pipe input_pipe(ios);
    bp::async_pipe output_pipe(ios);

    bp::child c(bin, "arg1", "arg2", "arg3", bp::std_out > output_pipe,
        bp::std_in < input_pipe, ios, bp::start_dir(wd.c_str()));

    size_t size = 8192;
    string input;

    vector <char> buffer(size);
    boost::system::error_code ec;

    std::error_code err;

    ios.run();
    while (1)
    {
        //show read whatever is available from the childs output_pipe
        read_loop_async(output_pipe, bp::buffer(buffer), err);



        cout << "\nBoot-> ";
        cin >> input;
        if (input == "1")
        {
            cout << "   send input to child: ";
            cin >> input;
            //send commands to the child, Program A
            //originally
            //sendToChild << input<< endl;
            write_pipe(input_pipe, bp::buffer(input));
        }
        if (input == "quit")
        {
            //sendToChild << input << endl;
            read_loop_async(output_pipe, bp::buffer(buffer), err);
            break;
        }

        ios.poll(ec);
        ios.restart();

    }

    c.join();
    cout << "done...";
    cin >> input;
}



这是我遵循的链接: 如何尽快检索程序输出印刷?

I ran into a roadblock. The code below has issues, but this is just a demo; I want to get the high level logic correct first.

The two startup application output a lot of startup info, before arriving the the "ready" state. At this state, Program A is ready for user input via stdin. Program B just listens via network connection--ingest and record data.

Ideally, with this sample program, I should be able to see the output from Program B, in "real-time". But at each loop iteration, nothing happens; I'm not sure it's receiving input via its pipe.

I was previously using bp::opstream to write to the child's--Program A--stdin. I know if some command are accepted to by Program A via its async_pipe, Progam B show also show some logging info (e.g. "trip"). These are window console applications, and I'm using Boost C++ to interact with them as child processes.

Does anyone have any ideas what's going on?



std::size_t read_loop(bp::async_pipe& p, mutable_buffer buf, boost::system::error_code &err)
{
    return p.read_some(buf, err);


}



void read_loop_async(bp::async_pipe& p, mutable_buffer buf, std::error_code &err) {
    p.async_read_some(buf, [&p, buf, &err](std::error_code ec, size_t n) {
        std::cout << "Received " << n << " bytes (" << ec.message() << "): '";
        std::cout.write(boost::asio::buffer_cast<char const*>(buf), n) << std::endl;
        err = ec;

        if (!ec)
            read_loop_async(p, buf, err);

    });
}


void write_pipe(bp::async_pipe&p, mutable_buffer buf)
{
    ba::async_write(p, buf, [](boost::system::error_code ec, std::size_t sz)
    {
        std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
    });

}


int main()
{

    bp::opstream sendToChild;
    string wd = "<---path-to-working-dir----->";
    ba::io_service ios;
    string bin = "<path-to-bin-and-name>";


    bp::async_pipe input_pipe(ios);
    bp::async_pipe output_pipe(ios);

    bp::child c(bin, "arg1", "arg2", "arg3", bp::std_out > output_pipe,
        bp::std_in < input_pipe, ios, bp::start_dir(wd.c_str()));

    size_t size = 8192;
    string input;

    vector <char> buffer(size);
    boost::system::error_code ec;

    std::error_code err;

    ios.run();
    while (1)
    {
        //show read whatever is available from the childs output_pipe
        read_loop_async(output_pipe, bp::buffer(buffer), err);



        cout << "\nBoot-> ";
        cin >> input;
        if (input == "1")
        {
            cout << "   send input to child: ";
            cin >> input;
            //send commands to the child, Program A
            //originally
            //sendToChild << input<< endl;
            write_pipe(input_pipe, bp::buffer(input));
        }
        if (input == "quit")
        {
            //sendToChild << input << endl;
            read_loop_async(output_pipe, bp::buffer(buffer), err);
            break;
        }

        ios.poll(ec);
        ios.restart();

    }

    c.join();
    cout << "done...";
    cin >> input;
}



Here is the link I followed:
How to retrieve program output as soon as it printed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

自此以后,行同陌路 2025-01-30 00:21:20

唔。有很多东西要拆箱。首先:

ios.run();

奔跑直到孩子完成。如果子进程需要发送比缓冲区中的拟合更多的输出,则可能不会消耗> ios.run(),这很可能僵局。

下一个poll()根据定义没有任何操作,因为您没有首先调用restart。幸运的是,您忽略了错误代码,重新启动接下来发生。

然后,您会得到下一个问题,因为循环的下一个迭代以 read_loop_async(output_pipe,bp :: buffer(buffer),err),err);表示,这意味着您,这意味着您具有通常禁止禁止的重叠读取操作( nofollow noreferrer“> nofellow noreferrer”> nofected行为)您正在使用相同的缓冲区。

这本身就足以解释“丢失的数据”,因为是的,您在同一位置进行了多个读数,因此一个人会抓住另一个。也就是说,如果您可以理由,因为您不能关于UB的原因。

现在,我的眼睛斑点甚至是第三个调用read_loop_async。这没有道理。顾名思义,read_loop_async已经是 loop :完成后自称:

    if (!ec)
        read_loop_async(p, buf, err);

因此,只有1个调用可以预期。似乎您不掌握async _* initiation函数始终立即返回(因为操作已完成异步)。您分配了以下事实:

    err = ec;

其中err是启动函数的参考参数。它不是那样工作的。该错误仅在完成时可用。由于无论如何您似乎都不会在读取循环之外使用它,所以我将其丢弃。

然后,它

        sendToChild << input << std::endl;

绝对什么都不做,因为sendtochild实际上仅声明了,并且从未在其他地方使用。

write_pipe再次尝试使用async _ initiation,但不能,因为它是在同步输入循环中使用的。只是不要在那里使用异步。正如编写的那样,它是UB的另一个来源,因为buf参数将指向std :: String变量,该变量正在主要函数中突变。因此,简化:

void write_pipe(bp::async_pipe& p, const_buffer buf) {
    error_code ec;
    auto       sz = write(p, buf, ec);
    std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
}

[注意它如何正确标记bufconst_buffer。]

现在,可能修复该sendToChild

  • 也可以通过关闭管道(信号传动) 使用对孩子的eof)
  • 打破输入循环
    if (input == "quit") {
        write_pipe(input_pipe, bp::buffer(input + "\n"));
        input_pipe.close();
        break;
    }

,我将用ios.restart()用Just poll() - )无论如何还为时过早。

除上述内容外,我用运算符替换了std :: getline调用 ,因为您很可能希望用户输入使用Enter键,而不是SPACE将用户输入界定。我还添加了“ \ n”,就像您在sendToChild行中一样,因为它有助于使用使用线条缓冲输入的简单测试子来演示。

现在,我们将使用它作为测试孩子:

bp::child c(bin, "-c",
            "time while read line; do echo \"$line\" | rev | xxd; done", //
            bp::std_out > output_pipe,
            bp::std_in < input_pipe, //
            ios,                     //
            bp::start_dir(wd));

这意味着我们将输入反向回声和十六进制转储,以及最后的时间摘要。

清单

live on Coliru

#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
namespace bp = boost::process;
using boost::asio::const_buffer;
using boost::asio::mutable_buffer;
using boost::system::error_code;

void read_loop_async(bp::async_pipe& p, mutable_buffer buf) {
    p.async_read_some(buf, [&p, buf](std::error_code ec, size_t n) {
        std::cout << "Received " << n << " bytes (" << ec.message() << "): '";
        std::cout.write(boost::asio::buffer_cast<char const*>(buf), n) << std::endl;

        if (!ec)
            read_loop_async(p, buf);
    });
}

void write_pipe(bp::async_pipe& p, const_buffer buf) {
    error_code ec;
    auto       sz = write(p, buf, ec);
    std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
}

int main() {
    std::string wd = "/home/sehe/Projects/stackoverflow";
    boost::asio::io_service ios;
    std::string bin = "/bin/bash";


    bp::async_pipe input_pipe(ios);
    bp::async_pipe output_pipe(ios);

    bp::child c(bin, "-c",
                "while read line; do echo \"$line\" | rev | xxd; done", //
                bp::std_out > output_pipe,
                bp::std_in < input_pipe, //
                ios,                     //
                bp::start_dir(wd));


    // Single invocation!
    std::vector<char> buffer(8192);
    read_loop_async(output_pipe, bp::buffer(buffer));

    std::cout << "\nBoot-> ";
    for (std::string input; getline(std::cin, input);
         std::cout << "\nBoot-> ") {
        if (input == "1") {
            std::cout << "   send input to child: ";
            if (getline(std::cin, input)) {
                write_pipe(input_pipe, bp::buffer(input + "\n"));
            }
        }
        if (input == "quit") {
            write_pipe(input_pipe, bp::buffer(input + "\n"));
            input_pipe.close();
            break;
        }

        ios.poll();
    }

    ios.run(); // effectively like `c.wait();` but async

    std::cout << "done...";
    // ignore until line end
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

有点固定的

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_{system,filesystem} && ./a.out <<HERE
1
Hello world
Ignored
1
Bye world
quit
HERE

Boot->    send input to child: Size Written 12 Ec: system:0 Success

Boot-> 
Boot->    send input to child: Size Written 10 Ec: system:0 Success

Boot-> Size Written 5 Ec: system:0 Success
Received 64 bytes (Success): '00000000: 646c 726f 7720 6f6c 6c65 480a            dlrow olleH.

Received 62 bytes (Success): '00000000: 646c 726f 7720 6579 420a                 dlrow eyB.

Received 57 bytes (Success): '00000000: 7469 7571 0a                             tiuq.

Received 0 bytes (End of file): '
done...

在我的系统上更易于互动:

”在此处输入图像说明”

Hmm. There's a lot to unpack. First off:

ios.run();

runs until the child completes. It might well deadlock if the child process needs to send more output than fits in the buffers, sinc ye you don't consume any of it before doing ios.run().

The next poll() by definition does not do anything, because you didn't call restart first. Luckily you ignore the error codes and restart happens next.

Then, you get the next problem, because the next iteration of the loop starts with another read_loop_async(output_pipe, bp::buffer(buffer), err); which means you have overlapping read operations which is usually forbidden (Undefined Behaviour), but runs into UB anyways here because you're using the same buffers.

This in itself is more than enough to explain "lost data" since, yeah, you're doing multiple reads in the same location, so one would clobber the other. That is, if you could reason about it, because you cannot reason about UB.

Wierdly now my eye spots even a third invocation of read_loop_async. It makes no sense. As the name suggests, read_loop_async is already a loop: it calls itself when completed:

    if (!ec)
        read_loop_async(p, buf, err);

So, only 1 invocation would ever be expected. It seems like you don't grasp that async_* initiation functions always return immediately (because the operation completes asynchronously). This is also exemplified in the fact that you assign:

    err = ec;

Where err is a reference argument to the initiation function. It doesn't work like that. The error is only available on completion. Since you don't seem to use it outside the read loop anyways, I'll drop it.

Then there's

        sendToChild << input << std::endl;

Which does absolutely nothing, since sendToChild is literally only declared, and never used elsewhere.

write_pipe again tries to use an async_ initiation, but it cannot, because it's being used in a synchronous input loop. Just don't use async there. As written it was another source of UB, because the buf argument would point to a std::string variable that was being mutated in the main function. So, simplify:

void write_pipe(bp::async_pipe& p, const_buffer buf) {
    error_code ec;
    auto       sz = write(p, buf, ec);
    std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
}

[Note how it correctly marks buf as const_buffer.]

Now, probably fix that sendToChild use by

  • also closing the pipe (signaling EOF to the child)
  • breaking the input loop
    if (input == "quit") {
        write_pipe(input_pipe, bp::buffer(input + "\n"));
        input_pipe.close();
        break;
    }

I'll replace the ios.restart() stuff with just poll() - because we didn't run() it too early anyways.

Other than the above, I replaced the operator>> with std::getline calls because it's most likely you want the user input to be delimited with Enter keys, not space. I also added "\n" as you had in the sendToChild line, because it helps demonstrating with a simple test child that uses line-buffered input.

Now, we'll use this as a test child:

bp::child c(bin, "-c",
            "time while read line; do echo \"$line\" | rev | xxd; done", //
            bp::std_out > output_pipe,
            bp::std_in < input_pipe, //
            ios,                     //
            bp::start_dir(wd));

Which means we get our input echoed in reverse and hex dump, and a time summary at the end.

Somewhat Fixed Listing

Live On Coliru

#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <iostream>
namespace bp = boost::process;
using boost::asio::const_buffer;
using boost::asio::mutable_buffer;
using boost::system::error_code;

void read_loop_async(bp::async_pipe& p, mutable_buffer buf) {
    p.async_read_some(buf, [&p, buf](std::error_code ec, size_t n) {
        std::cout << "Received " << n << " bytes (" << ec.message() << "): '";
        std::cout.write(boost::asio::buffer_cast<char const*>(buf), n) << std::endl;

        if (!ec)
            read_loop_async(p, buf);
    });
}

void write_pipe(bp::async_pipe& p, const_buffer buf) {
    error_code ec;
    auto       sz = write(p, buf, ec);
    std::cout << "Size Written " << sz << " Ec: " << ec << " " << ec.message() << '\n';
}

int main() {
    std::string wd = "/home/sehe/Projects/stackoverflow";
    boost::asio::io_service ios;
    std::string bin = "/bin/bash";


    bp::async_pipe input_pipe(ios);
    bp::async_pipe output_pipe(ios);

    bp::child c(bin, "-c",
                "while read line; do echo \"$line\" | rev | xxd; done", //
                bp::std_out > output_pipe,
                bp::std_in < input_pipe, //
                ios,                     //
                bp::start_dir(wd));


    // Single invocation!
    std::vector<char> buffer(8192);
    read_loop_async(output_pipe, bp::buffer(buffer));

    std::cout << "\nBoot-> ";
    for (std::string input; getline(std::cin, input);
         std::cout << "\nBoot-> ") {
        if (input == "1") {
            std::cout << "   send input to child: ";
            if (getline(std::cin, input)) {
                write_pipe(input_pipe, bp::buffer(input + "\n"));
            }
        }
        if (input == "quit") {
            write_pipe(input_pipe, bp::buffer(input + "\n"));
            input_pipe.close();
            break;
        }

        ios.poll();
    }

    ios.run(); // effectively like `c.wait();` but async

    std::cout << "done...";
    // ignore until line end
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Tested with

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_{system,filesystem} && ./a.out <<HERE
1
Hello world
Ignored
1
Bye world
quit
HERE

Prints

Boot->    send input to child: Size Written 12 Ec: system:0 Success

Boot-> 
Boot->    send input to child: Size Written 10 Ec: system:0 Success

Boot-> Size Written 5 Ec: system:0 Success
Received 64 bytes (Success): '00000000: 646c 726f 7720 6f6c 6c65 480a            dlrow olleH.

Received 62 bytes (Success): '00000000: 646c 726f 7720 6579 420a                 dlrow eyB.

Received 57 bytes (Success): '00000000: 7469 7571 0a                             tiuq.

Received 0 bytes (End of file): '
done...

Which is easier to follow interactively on my system:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文