Может ли popen() создавать двунаправленные каналы, такие как pipe() + fork ()?

StackOverflow https://stackoverflow.com/questions/3884103

  •  28-09-2019
  •  | 
  •  

Вопрос

Я реализую конвейер в моделируемой файловой системе на C ++ (в основном на C).Ему необходимо запускать команды в командной строке хоста, но выполнять сам конвейер в моделируемой файловой системе.

Я мог бы достичь этого с помощью pipe(), fork(), и system() системные вызовы, но я бы предпочел использовать popen() (который обрабатывает создание канала, разветвление процесса и передачу команды в оболочку).Это может быть невозможно, потому что (я думаю) мне нужно иметь возможность записывать из родительского процесса канала, читать в конце дочернего процесса, записывать выходные данные обратно из дочернего процесса и, наконец, читать эти выходные данные из родительского.Справочная страница для popen() в моей системе указано, что двунаправленный канал возможен, но мой код должен выполняться в системе с более старой версией, поддерживающей только однонаправленные каналы.

С помощью отдельных вызовов, приведенных выше, я могу открывать / закрывать каналы для достижения этой цели.Возможно ли это с popen()?

Для тривиального примера, чтобы запустить ls -l | grep .txt | grep cmds Мне нужно:

  • Откройте канал и процесс для запуска ls -l на хосте;перечитайте его выходные данные обратно
  • Трубопровод на выходе ls -l возвращаюсь к моему симулятору
  • Откройте канал и процесс для запуска grep .txt на хосте, на передаваемом по каналу выводе ls -l
  • Передайте результат этого обратно в симулятор (застрял здесь).
  • Откройте канал и процесс для запуска grep cmds на хосте, на передаваемом по каналу выводе grep .txt
  • Передайте выходные данные этого файла обратно в симулятор и распечатайте их

мужчина попен

Из Mac OS X:

Тот Самый popen() функция "открывает" процесс путем создания двунаправленного канала, разветвления и вызова оболочки.Любые потоки, открытые предыдущими popen() вызовы в родительском процессе закрываются в новом дочернем процессе.Исторически Сложилось, popen() был реализован с однонаправленной трубой;следовательно, многие реализации popen() только разрешите аргументу mode указывать чтение или запись, но не то и другое одновременно.Потому что popen() теперь реализован с использованием двунаправленного канала, аргумент mode может запрашивать двунаправленный поток данных.Аргумент mode является указателем на завершающуюся нулем строку, которая должна быть 'r' для чтения, 'w' для записи или 'r+' для чтения и записи.

Это было полезно?

Решение

Вы, кажется, ответили на ваш собственный вопрос. Если ваш код должен работать над более старой системой, которая не поддерживает popen Открытие двунаправленных труб, тогда вы не сможете использовать popen (по крайней мере, не тот, который поставляется).

Реальный вопрос будет о точных возможностях поставленных более старых систем. В частности, их pipe Поддержка создания двунаправленных труб? Если у них есть pipe которые могут создать двунаправленную трубу, но popen это не так, я бы написал основной поток кода для использования popen с двунаправленной трубой и поставьте реализацию popen Это может использовать двунаправленную трубу, которая скомпилирована в использованном, где это необходимо.

Если вам нужно поддерживать системы достаточно взрослых, чтобы pipe только поддерживает однонаправленные трубы, тогда вы в значительной степени застряли с использованием pipe, fork, dup2, и т. Д., Сам по себе. Я, вероятно, все еще обернув это в функции, которая работает почти как современная версия popen, но вместо того, чтобы возвращать одну ручку файлов, заполняет небольшую структуру двумя ручками файлов, один для ребенка stdin, другой для ребенка stdout.

Другие советы

Я бы предложил писать свою собственную функцию, чтобы сделать трубопроводы / наложить / систему для вас. Вы можете получить функцию создавать процесс и возвращать файловые дескрипторы чтения / записи, как в ...

typedef void pfunc_t (int rfd, int wfd);

pid_t pcreate(int fds[2], pfunc_t pfunc) {
    /* Spawn a process from pfunc, returning it's pid. The fds array passed will
     * be filled with two descriptors: fds[0] will read from the child process,
     * and fds[1] will write to it.
     * Similarly, the child process will receive a reading/writing fd set (in
     * that same order) as arguments.
    */
    pid_t pid;
    int pipes[4];

    /* Warning: I'm not handling possible errors in pipe/fork */

    pipe(&pipes[0]); /* Parent read/child write pipe */
    pipe(&pipes[2]); /* Child read/parent write pipe */

    if ((pid = fork()) > 0) {
        /* Parent process */
        fds[0] = pipes[0];
        fds[1] = pipes[3];

        close(pipes[1]);
        close(pipes[2]);

        return pid;

    } else {
        close(pipes[0]);
        close(pipes[3]);

        pfunc(pipes[2], pipes[1]);

        exit(0);
    }

    return -1; /* ? */
}

Вы можете добавить любые функциональные возможности, которые вам нужны там.

Posix предусматривает, что popen() Вызов не предназначен для обеспечения двунаправленной связи:

Аргумент режима для POPEN () - это строка, которая указывает режим ввода / вывода:

  1. Если режим r, когда начнется дочерний процесс, его дескриптор файла stdout_fileno должен быть записью концом трубы, а файловый дескриптор fileeno (поток) в процессе вызова, где поток - указатель потока, возвращаемый popen (), должен быть читаемый конец трубы.
  2. Если режим W, когда ребенок будет запущен, его дескриптор файла stdin_fileno должен быть читабельным концом трубы, а файловый дескриптор fileeno (поток) в процессе вызова, где поток - указатель потока, возвращаемый popen (), должен быть быть пиремным концом трубы.
  3. Если режим является любое другое значение, результат не указан.

Любой портативный код не приведет к этому не допущению. BSD popen() похож на то, что описывает ваш вопрос.

Кроме того, трубы отличаются от розетки, и каждый дескриптор файлов труб является Uni-Disional. Вам придется создать два труба, которые настроили для каждого направления.

В одном из чистое решение я обращаюсь к скрипту, и поэтому мне нужно написать в его stdin и прочитать из его stdout.Следующая функция выполняет команду с перенаправлением stdin и stdout в канал.Вы можете использовать его и адаптировать по своему вкусу.

static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
    int p1[2], p2[2];

    if (!pid || !infd || !outfd)
        return false;

    if (pipe(p1) == -1)
        goto err_pipe1;
    if (pipe(p2) == -1)
        goto err_pipe2;
    if ((*pid = fork()) == -1)
        goto err_fork;

    if (*pid) {
        /* Parent process. */
        *infd = p1[1];
        *outfd = p2[0];
        close(p1[0]);
        close(p2[1]);
        return true;
    } else {
        /* Child process. */
        dup2(p1[0], 0);
        dup2(p2[1], 1);
        close(p1[0]);
        close(p1[1]);
        close(p2[0]);
        close(p2[1]);
        execvp(*command, command);
        /* Error occured. */
        fprintf(stderr, "error running %s: %s", *command, strerror(errno));
        abort();
    }

err_fork:
    close(p2[1]);
    close(p2[0]);
err_pipe2:
    close(p1[1]);
    close(p1[0]);
err_pipe1:
    return false;
}

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46

(Я использовал тот же код в всплывающее окно одновременного чтения и записи)

Не нужно создавать два труба и тратить подачу заявления в каждый процесс. Просто используйте сокет вместо этого. https://stackoverflow.com/a/25177958/894520.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top