Question

Je mise en œuvre de la tuyauterie sur un système de fichiers simulé en C ++ (avec la plupart du temps C). Il a besoin d'exécuter des commandes dans le shell hôte, mais effectuer la tuyauterie lui-même sur le système de fichiers simulé.

Je pourrais y parvenir avec les pipe(), fork() et appels système system(), mais je préfère utiliser popen() (qui gère la création d'un tuyau, bifurquer un processus, et en passant une commande à la coquille). Cela peut ne pas être possible parce que (je pense) que je dois être en mesure d'écrire du processus parent du tuyau, lisez ce qui suit la fin du processus de l'enfant, écrire le dos de sortie de l'enfant, et enfin lire que la sortie du parent. La page de manuel pour popen() sur mon système dit un tube bidirectionnel est possible, mais mon code doit fonctionner sur un système avec une ancienne version supportant des tuyaux uniquement unidirectionnels.

Avec les appels distincts ci-dessus, je peux ouvrir / tuyaux à proximité pour y parvenir. Est-ce possible avec popen()?

Pour un exemple trivial, pour exécuter ls -l | grep .txt | grep cmds je besoin:

  • Ouvrir un tuyau et un processus pour l'exécution ls -l sur l'hôte; lire le dos de sortie
  • tuyau la sortie arrière ls -l à mon simulateur
  • Ouvrir un tuyau et un processus pour l'exécution grep .txt sur l'hôte sur la sortie courante de ls -l
  • tuyau la sortie de ce retour au simulateur (coincé ici)
  • Ouvrir un tuyau et un processus pour l'exécution grep cmds sur l'hôte sur la sortie courante de grep .txt
  • tuyau la sortie de ce retour au simulateur et l'imprimer

homme popen

A partir de Mac OS X:

  

La fonction popen() « ouvre » un   Procédé de création d'un bidirectionnelle   pipe, bifurquer, et en invoquant la coquille.   Tous les cours d'eau ouverts par popen() précédent   appels dans le processus parent sont fermés   dans le nouveau processus enfant.   Historiquement, popen() a été mis en œuvre   avec un tuyau unidirectionnel; Par conséquent,   de nombreuses implémentations de popen() seulement   permettre l'argument de mode pour spécifier   lecture ou l'écriture, pas les deux. Car   popen() est maintenant mis en œuvre au moyen d'un   tuyau bidirectionnel, l'argument de mode   peut demander un flux de données bidirectionnel.   L'argument de mode est un pointeur vers un   null chaîne à terminaison qui doit être   « R » pour la lecture, « w » pour l'écriture, ou   'R +' pour la lecture et l'écriture.

Était-ce utile?

La solution

Vous semblez avoir répondu à votre propre question. Si vos besoins de code à travailler sur un ancien système qui ne supporte pas les tubes bidirectionnels d'ouverture de popen, alors vous ne serez pas en mesure d'utiliser popen (au moins pas celui qui est fourni).

La vraie question serait sur les capacités exactes des anciens systèmes en question. En particulier, ne leur soutien à la création pipe tubes bidirectionnels? Si elles ont un pipe qui peut créer un tube bidirectionnel, mais cela ne popen pas, alors j'écrire le flux principal du code à utiliser popen avec un tuyau bidirectionnel, et fournir une implémentation de popen qui peut utiliser un tube bidirectionnel qui obtient compilé dans un utilisé si nécessaire.

Si vous avez besoin de soutenir les systèmes assez vieux que pipe ne supporte que les tuyaux unidirectionnels, alors vous êtes un peu coincé à l'utilisation pipe, fork, dup2, etc., vous-même. Je serais probablement envelopper encore cela dans une fonction qui fonctionne presque comme une version moderne de popen, mais au lieu de retourner une poignée de fichier, remplissages dans une petite structure avec deux poignées de fichiers, un pour les années de l'enfant stdin, l'autre pour le stdout de l'enfant.

Autres conseils

Je vous suggère d'écrire votre propre fonction pour faire la tuyauterie / fork / système-ing pour vous. Vous pourriez avoir la fonction spawn un processus et retour lecture / descripteurs de fichiers d'écriture, comme dans ...

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

Vous pouvez ajouter toutes les fonctionnalités que vous avez besoin là-bas.

Posix stipule que l'appel popen() n'est pas conçu pour fournir deux fois communication-directionnel:

  

L'argument de sélection sur popen () est une chaîne qui spécifie le mode d'E / S:

     
      
  1. Si le mode est r, lorsque le processus enfant est démarré, le descripteur de fichier STDOUT_FILENO doit être la fin inscriptible du tube, et le descripteur de fichier fileno (flux) dans le processus d'appel, où le ruisseau est le pointeur de flux renvoyé par popen (), est la fin lisible du tuyau.
  2.   
  3. Si le mode est w, lorsque le processus enfant commence son descripteur de fichier STDIN_FILENO est la fin lisible du tube, et le descripteur de fichier fileno (flux) dans le processus d'appel, où le ruisseau est le pointeur de flux retourné par popen ( ), est la fin inscriptible du tuyau.
  4.   
  5. Si le mode est une autre valeur, le résultat est non spécifiée.
  6.   

Tout code portable fera aucune hypothèse au-delà. Le popen() BSD est similaire à ce que votre question décrit.

En outre, les tuyaux sont différents des douilles et chaque descripteur de fichier de tuyau est unidirectionnel. Il faudrait créer deux tuyaux, l'un configuré pour chaque direction.

Dans l'un des backends netresolve Je parle à un script et donc je dois écrire son stdin et lu de son stdout. La fonction suivante exécute une commande avec stdin et stdout redirigé vers un tuyau. Vous pouvez l'utiliser et l'adapter à votre goût.

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

(I utilisé le même code dans popen lecture et écriture simultanée )

besoin Non pour créer deux tuyaux et perdre FileDescriptor dans chaque processus. Il suffit d'utiliser une prise à la place. https://stackoverflow.com/a/25177958/894520

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top