Czy popen() może tworzyć dwukierunkowe rury takie jak pipe () + fork ()?

Implementuję piping na symulowanym systemie plików w C++ (głównie z C). Musi uruchamiać polecenia w powłoce hosta, ale sam wykonuje Orurowanie w symulowanym systemie plików.

Mógłbym to osiągnąć z pipe(), fork(), i system() wywołania systemowe, ale wolałbym użyć popen() (która zajmuje się tworzeniem potoku, rozwidlaniem procesu i przekazywaniem komend do powłoki). Może to nie być możliwe ,ponieważ (myślę) muszę być w stanie pisać z procesu macierzystego rury, odczytaj koniec procesu potomnego, Zapisz wyjście z powrotem od dziecka, a na koniec odczytaj to wyjście od rodzica. Strona podręcznika dla popen() w moim systemie mówi, że dwukierunkowa rura jest możliwa, ale mój kod musi działać na systemie ze starszą wersją obsługującą tylko jednokierunkowe rury.

Dzięki oddzielnym wywołaniom powyżej mogę otwierać/zamykać rury, aby to osiągnąć. Czy to możliwe z popen()?

Dla trywialnego przykładu, aby uruchomić ls -l | grep .txt | grep cmds muszę:

    Po uruchomieniu rura, proces może być uruchomiony na serwerze.]}
  • Przełącz wyjście ls -l z powrotem do mojego symulatora
  • Otwórz rurę i uruchom proces grep .txt na hoście na wyjściu rurociągu ls -l
  • W tym samym czasie, w grze pojawiają się nowe elementy.]} W tym celu należy otworzyć rurę i uruchomić proces grep cmds na hoście na wyjściu rurociągu grep .txt
  • Przełącz wyjście tego z powrotem do symulatora i wydrukuj it

Man popen

Z Mac OS X:

Funkcja popen() 'otwiera' a proces poprzez utworzenie dwukierunkowego rura, rozwidlenie i wywołanie powłoki. Wszystkie strumienie otwarte przez poprzedni popen() wywołania procesu nadrzędnego są zamykane w procesie nowego dziecka. Historycznie, popen() został zaimplementowany z rurką jednokierunkową; stąd, wiele implementacji popen() tylko Zezwalaj argumentowi mode na określenie czytanie czy pisanie, nie jedno i drugie. Ponieważ {[3] } jest teraz zaimplementowany przy użyciu rura dwukierunkowa, argument mode może zażądać dwukierunkowego przepływu danych. Argument mode jest wskaźnikiem do null-zakończony łańcuch, który musi być "r" do czytania, " w " do pisania, lub "r+" do czytania i pisania.

Author: Taylor Edmiston, 2010-10-07

5 answers

Wygląda na to, że odpowiedziałeś na swoje pytanie. Jeśli twój kod musi działać na starszym systemie, który nie obsługuje popen otwierania dwukierunkowych rur, nie będziesz mógł użyć popen (przynajmniej nie tego, który jest dostarczony).

[14]}prawdziwym pytaniem byłoby dokładne możliwości starszych systemów, o których mowa. W szczególności, czy ich pipe wspiera tworzenie dwukierunkowych rur? Jeśli mają pipe, które mogą tworzyć dwukierunkową rurę, ale {[0] } to nie, to Zapisuje główny strumień kodu do użycia popen z dwukierunkowym strumieniem i dostarcza implementację popen, która może używać dwukierunkowego potoku, który jest kompilowany w używanym tam, gdzie jest to potrzebne.

Jeśli potrzebujesz obsługiwać systemy na tyle stare, że pipe obsługuje tylko jednokierunkowe rury, to praktycznie utknąłeś w używaniu pipe, fork, dup2, itd., na własną rękę. Prawdopodobnie nadal zawijałbym to w funkcję, która działa prawie {[20] } jak nowoczesna wersja popen, ale zamiast zwracając jeden uchwyt do pliku, wypełnia małą strukturę dwoma uchwytami do plików, jeden dla dziecka stdin, drugi dla dziecka stdout.

 10
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 17:32:18

Sugerowałbym napisanie własnej funkcji do robienia rurociągów / widełek / system-ing dla Ciebie. Możesz mieć funkcję spawn proces i zwracać deskryptory odczytu/zapisu plików, jak w...

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; /* ? */
}

Możesz tam dodać dowolną funkcjonalność.

 19
Author: slezica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 17:55:42

POSIX przewiduje, że popen() wywołanie nie jest zaprojektowane do zapewnienia komunikacji dwukierunkowej:

Argument mode do popen() jest łańcuchem znaków określającym tryb wejścia / Wyjścia:

  1. jeśli trybem jest R, to gdy proces potomny jest uruchomiony, jego deskryptor pliku STDOUT_FILENO będzie zapisywalnym końcem potoku, a deskryptor pliku fileno(stream) w procesie wywołującym, gdzie stream jest wskaźnikiem strumienia zwracanym przez popen(), będzie czytelnym końcem potoku. rura.
  2. jeśli tryb jest w, to podczas uruchamiania procesu potomnego jego deskryptor pliku STDIN_FILENO będzie czytelnym końcem potoku, a deskryptor pliku fileno (stream) w procesie wywołującym, gdzie stream jest wskaźnikiem strumienia zwracanym przez popen (), będzie zapisywalnym końcem potoku.
  3. jeśli mode jest inną wartością, wynik jest nieokreślony.

Każdy przenośny Kod nie będzie miał żadnych założeń poza tym. BSD popen() jest podobny do tego, co twoje pytanie opisuje.

Dodatkowo rury różnią się od gniazd i każdy deskryptor pliku rury jest jednokierunkowy. Trzeba by utworzyć dwie rury, po jednej skonfigurowanej dla każdego kierunku.

 8
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 18:12:17

Nie ma potrzeby tworzenia dwóch rur i marnowania filedescriptor w każdym procesie. Zamiast tego użyj gniazdka. https://stackoverflow.com/a/25177958/894520

 1
Author: hyc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:43

W jednym z backendów netresolve rozmawiam ze skryptem i dlatego muszę pisać na jego stdin i czytać z jego stdout. Poniższa funkcja wykonuje polecenie z stdin i stdout przekierowanymi do potoku. Możesz go użyć i dostosować do swoich upodobań.

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

(użyłem tego samego kodu w popen jednoczesny odczyt i zapis )

 0
Author: Pavel Šimerda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:51