Захват дочернего процесса с помощью libuv

Я использую libuv. Я прочитал http://nikhilm.github.com/uvbook/processes.html и по-прежнему не может решить, как захватить stdout дочернего процесса, чтобы он был доступен в родительском (но не вместо stdin родителя).

Мой код в настоящее время:

#include 
#include 
#include "../../libuv/include/uv.h"

uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
uv_pipe_t apipe;

void on_child_exit(uv_process_t *req, int exit_status, int term_signal) {
    fprintf(stderr, "Process exited with status %d, signal %d\n", exit_status, term_signal);
    uv_close((uv_handle_t*) req, NULL);
}

uv_buf_t alloc_buffer(uv_handle_t *handle, size_t len) {
    printf("alloc_buffer called\n");
    uv_buf_t buf;
    buf.base = malloc(len);
    buf.len = len;
    return buf;
}

void read_apipe(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) {
    printf("read %li bytes from the child process\n", nread);
}

int main(int argc, char *argv[]) {
    printf("spawn_test\n");
    loop = uv_default_loop();

    char* args[3];
    args[0] = "dummy";
    args[1] = NULL;
    args[2] = NULL;

    uv_pipe_init(loop, &apipe, 0);
    uv_pipe_open(&apipe, 0);

    options.stdio_count = 3;
    uv_stdio_container_t child_stdio[3];
    child_stdio[0].flags = UV_IGNORE;
    child_stdio[1].flags = UV_INHERIT_STREAM;
    child_stdio[1].data.stream = (uv_stream_t *) &apipe;
    child_stdio[2].flags = UV_IGNORE;
    options.stdio = child_stdio;

    options.exit_cb = on_child_exit;
    options.file = args[0];
    options.args = args;

    uv_read_start((uv_stream_t*)&apipe, alloc_buffer, read_apipe);
    if (uv_spawn(loop, &child_req, options)) {
        fprintf(stderr, "%s\n", uv_strerror(uv_last_error(loop)));
        return 1;
    }

    return uv_run(loop, UV_RUN_DEFAULT);
}

dummy.c:

#include 
#include 

int main() {
    printf("child starting\n");
    sleep(1);
    printf("child running\n");
    sleep(2);
    printf("child ending\n");
    return 0;
}

Я чувствую, что я до сих пор не совсем понимаю смысл труб Либува.

9

2 ответы

Я нашел решение:

  1. У меня были неправильные флаги, они должны были быть UV_CREATE_PIPE | UV_READABLE_PIPE не UV_INHERIT_STREAM .
  2. Мне нужно было вызвать uv_read_start после uv_spawn . Я предполагаю, что нет никакой потери данных, поскольку uv_run еще не вызван.
  3. Вышеупомянутые два исправления показали, что все выходные данные dummy поступают сразу, а не три куска (как в командной строке). fflush в dummy.c исправлено это.

spawn_test:

#include 
#include 
#include "../../libuv/include/uv.h"

uv_loop_t *loop;
uv_process_t child_req;
uv_process_options_t options;
uv_pipe_t apipe;

void on_child_exit(uv_process_t *req, int exit_status, int term_signal) {
    fprintf(stderr, "Process exited with status %d, signal %d\n", exit_status, term_signal);
    uv_close((uv_handle_t*) req, NULL);
}

uv_buf_t alloc_buffer(uv_handle_t *handle, size_t len) {
    printf("alloc_buffer called, requesting a %lu byte buffer\n");
    uv_buf_t buf;
    buf.base = malloc(len);
    buf.len = len;
    return buf;
}

void read_apipe(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) {
    printf("read %li bytes in a %lu byte buffer\n", nread, buf.len);
    if (nread + 1 > buf.len) return;
    buf.base[nread] = '\0';//turn it into a cstring
    printf("read: |%s|", buf.base);
}

int main(int argc, char *argv[]) {
    printf("spawn_test\n");
    loop = uv_default_loop();

    char* args[3];
    args[0] = "dummy";
    args[1] = NULL;
    args[2] = NULL;

    uv_pipe_init(loop, &apipe, 0);
    uv_pipe_open(&apipe, 0);

    options.stdio_count = 3;
    uv_stdio_container_t child_stdio[3];
    child_stdio[0].flags = UV_IGNORE;
    child_stdio[1].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
    child_stdio[1].data.stream = (uv_stream_t *) &apipe;
    child_stdio[2].flags = UV_IGNORE;
    options.stdio = child_stdio;

    options.exit_cb = on_child_exit;
    options.file = args[0];
    options.args = args;

    if (uv_spawn(loop, &child_req, options)) {
        fprintf(stderr, "%s\n", uv_strerror(uv_last_error(loop)));
        return 1;
    }
    uv_read_start((uv_stream_t*)&apipe, alloc_buffer, read_apipe);

    return uv_run(loop, UV_RUN_DEFAULT);
}

dummy.c:

#include 
#include 

int main() {
    printf("child starting\n");
    fflush(stdout);
    sleep(1);
    printf("child running\n");
    fflush(stdout);
    sleep(2);
    printf("child ending\n");
    fflush(stdout);
    return 0;
}
5
добавлено

Посмотрите, как они это делают в модульном тестировании libuv libuv /test/test-stdio-over-pipes.c :

  • Don't call uv_pipe_open
  • Flags for child's stdin: UV_CREATE_PIPE | UV_READABLE_PIPE
  • Flags for child's stdout and stderr: UV_CREATE_PIPE | UV_WRITABLE_PIPE

Существует также проблема в Windows, где uv_spawn может вернуться но даже если он столкнулся с ошибкой, и в этих случаях вам нужно проверить process.spawn_error , который существует только в Windows.

4
добавлено