Domanda

Sto implementando il piping su un file system simulato in C++ (principalmente C).Deve eseguire comandi nella shell host ma eseguire il piping stesso sul file system simulato.

Potrei raggiungere questo obiettivo con il pipe(), fork(), E system() chiamate di sistema, ma preferirei usare popen() (che gestisce la creazione di una pipe, il fork di un processo e il passaggio di un comando alla shell).Questo potrebbe non essere possibile perché (penso) devo essere in grado di scrivere dal processo genitore della pipe, leggere alla fine del processo figlio, riscrivere l'output dal figlio e infine leggere l'output dal genitore.La pagina man di popen() sul mio sistema dice che è possibile un tubo bidirezionale, ma il mio codice deve essere eseguito su un sistema con una versione precedente che supporta solo tubi unidirezionali.

Con le chiamate separate di cui sopra, posso aprire/chiudere i tubi per raggiungere questo obiettivo.È possibile con popen()?

Per un esempio banale, correre ls -l | grep .txt | grep cmds Ho bisogno di:

  • Aprire una pipe ed eseguire il processo ls -l sull'ospite;rileggere il suo output
  • Convoglia l'output di ls -l torno al mio simulatore
  • Aprire una pipe ed eseguire il processo grep .txt sull'host sull'output convogliato di ls -l
  • Riporta l'output di questo al simulatore (bloccato qui)
  • Aprire una pipe ed eseguire il processo grep cmds sull'host sull'output convogliato di grep .txt
  • Riporta l'output di questo al simulatore e stampalo

uomo papa

Da Mac OS X:

IL popen() funzione 'apre' un processo creando un sistema bidirezionale tubo, biforcazione e richiamo del guscio.Eventuali flussi aperti da precedenti popen() Le chiamate nel processo padre vengono chiuse nel processo di creazione di un nuovo figlio.Storicamente, popen() è stato attuato con tubo unidirezionale;quindi molte implementazioni di popen() soltanto Consenti all'argomento mode di specificare leggere o scrivere, non entrambe.Perché popen() è ora implementato utilizzando un pipe bidirezionale, l'argomento mode può richiedere un flusso di dati bidirezionale.L'argomento mode è un puntatore a un oggetto stringa con terminazione null che deve essere 'r' per leggere, 'w' per scrivere, o 'R+' per leggere e scrivere.

È stato utile?

Soluzione

Sembra che tu abbia risposto alla tua stessa domanda.Se il tuo codice deve funzionare su un sistema precedente che non supporta popen aprendo tubi bidirezionali, non sarà possibile utilizzarli popen (almeno non quello fornito).

La vera domanda riguarderebbe le esatte capacità dei vecchi sistemi in questione.In particolare, fa il loro pipe supporta la creazione di tubi bidirezionali?Se hanno un pipe che può creare un tubo bidirezionale, ma popen in caso contrario, scriverei il flusso principale del codice da utilizzare popen con un tubo bidirezionale e fornire un'implementazione di popen che può utilizzare una pipe bidirezionale che viene compilata in un used dove necessario.

Se hai bisogno di supportare sistemi abbastanza vecchi, quello pipe supporta solo tubi unidirezionali, quindi sei praticamente bloccato con l'utilizzo pipe, fork, dup2, ecc., da solo.Probabilmente lo concluderei comunque in una funzione che funzioni Quasi come una versione moderna di popen, ma invece di restituire un handle di file, riempie una piccola struttura con due handle di file, uno per quello del bambino stdin, l'altro per quello del bambino stdout.

Altri suggerimenti

Suggerirei di scrivere la tua funzione per eseguire il piping/fork/system-ing per te.Potresti fare in modo che la funzione generi un processo e restituisca descrittori di file di lettura/scrittura, come in...

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

Puoi aggiungere qualsiasi funzionalità di cui hai bisogno lì.

POSIX stabilisce che il popen() la chiamata non è progettata per fornire una comunicazione bidirezionale:

L'argomento mode di popen() è una stringa che specifica la modalità I/O:

  1. Se mode è r, quando viene avviato il processo figlio, il suo descrittore di file STDOUT_FILENO sarà l'estremità scrivibile della pipe e il descrittore di file fileno(stream) nel processo chiamante, dove stream è il puntatore di stream restituito da popen(), sarà l'estremità leggibile del tubo.
  2. Se mode è w, quando il processo figlio viene avviato, il suo descrittore di file STDIN_FILENO sarà la fine leggibile della pipe, e il descrittore di file fileno(stream) nel processo chiamante, dove stream è il puntatore di stream restituito da popen(), sarà essere l'estremità scrivibile del tubo.
  3. Se mode è un valore qualsiasi, il risultato non è specificato.

Qualsiasi codice portatile non farà ipotesi oltre a ciò.Il BSD popen() è simile a ciò che descrive la tua domanda.

Inoltre, le pipe sono diverse dai socket e ogni descrittore di file pipe è unidirezionale.Dovresti creare due tubi, uno configurato per ciascuna direzione.

In uno dei netresolve backends Sto parlando con uno script e quindi devo scrivere sul suo stdin e leggi dal suo stdout.La seguente funzione esegue un comando con stdin e stdout reindirizzati su una pipe.Puoi usarlo e adattarlo a tuo piacimento.

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

(Ho usato lo stesso codice in pop lettura e scrittura simultanee)

Non è necessario creare due pipe e sprecare un descrittore di file in ogni processo.Basta usare invece una presa. https://stackoverflow.com/a/25177958/894520

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top