Question

Doublons possibles :

J'ai essayé d'apprendre le piping sous Linux en utilisant dup/dup2 et fork ces 3 derniers jours.Je pense que j'ai compris, mais lorsque j'appelle deux programmes différents à partir du processus enfant, il semble que je ne capture que la sortie du premier appelé. Je ne comprends pas pourquoi et/ou ce que je fais de mal.C'est ma question principale.

Modifier:Je pense qu'une solution possible consiste à créer un autre enfant et à configurer des tuyaux avec dup2, mais je me demande simplement pourquoi le code ci-dessous ne fonctionne pas.Ce que je veux dire, c'est que je m'attendrais à capturer stderr du premier appel execl et stdout du second.Cela ne semble pas se produire.

Ma deuxième question est de savoir si j'ouvre et ferme correctement les tuyaux.Sinon, j'aimerais savoir ce que je dois ajouter/supprimer/modifier.

Voici mon code :

#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

void parentProc(int* stdoutpipe, int* stderrpipe);
void childProc(int* stdoutpipe, int* stderrpipe);

int main(){
    pid_t pid;
    int status;

    int stdoutpipe[2]; // pipe
    int stderrpipe[2]; // pipe

    // create a pipe
    if (pipe(stdoutpipe) || pipe(stderrpipe)){
        std::cerr << "Pipe failed." << std::endl;
        return EXIT_FAILURE;
    }

    // fork a child
    pid = fork();
    if (pid < 0) {
        std::cerr << "Fork failed." << std::endl;
        return EXIT_FAILURE;
    } 
    // child process
    else if (pid == 0){
        childProc(stdoutpipe, stderrpipe);  

    }
    // parent process
    else {
        std::cout<< "waitpid: " << waitpid(pid, &status, 0)
             <<'\n'<<std::endl;
        parentProc(stdoutpipe, stderrpipe);
    }

    return 0;
}



void childProc(int* stdoutpipe, int* stderrpipe){
    dup2(stdoutpipe[WRITE_END], STDOUT_FILENO);
    close(stdoutpipe[READ_END]);

    dup2(stderrpipe[WRITE_END], STDERR_FILENO);
    close(stderrpipe[READ_END]);

    execl("/bin/bash", "/bin/bash", "foo", NULL);
    execl("/bin/ls", "ls", "-1", (char *)0);

    //  execl("/home/me/outerr", "outerr", "-1", (char *)0);

    //char * msg = "Hello from stdout";
    //std::cout << msg;

    //msg = "Hello from stderr!";
    //std::cerr << msg << std::endl;

    // close write end now?
}

void parentProc(int* stdoutpipe, int* stderrpipe){
    close(stdoutpipe[WRITE_END]);
    close(stderrpipe[WRITE_END]);

    char buffer[256];
    char buffer2[256];
    read(stdoutpipe[READ_END], buffer, sizeof(buffer));
    std::cout << "stdout: " << buffer << std::endl;

    read(stderrpipe[READ_END], buffer2, sizeof(buffer));
    std::cout << "stderr: " << buffer2 << std::endl;

    // close read end now?
}

Lorsque je lance ceci, j'obtiens le résultat suivant :

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21423

stdout: hB�(6
stderr: foo: line 1: -bash:: command not found

Le code source du binaire "outerr" (commenté ci-dessus) est simplement :

#include <iostream>

int main(){
    std::cout << "Hello from stdout" << std::endl;
    std::cerr << "Hello from stderr!" << std::endl;
    return 0;
}

Lorsque j'appelle "outerr", au lieu de ls ou "foo", j'obtiens le résultat suivant, auquel je m'attendrais :

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21439

stdout: Hello from stdout

stderr: Hello from stderr!
Était-ce utile?

La solution

Sur execl

Une fois que vous avez appelé avec succès execl ou toute autre fonction du exec famille, le processus original est complètement écrasé par le nouveau processus.Cela implique que le nouveau processus ne « revient » jamais à l’ancien.Si tu en as deux execl appels consécutifs, la seule façon d'exécuter le second est si le premier échoue.

Pour exécuter deux commandes différentes à la suite, vous devez fork un enfant pour exécuter la première commande, wait, fork un deuxième enfant pour exécuter la deuxième commande, puis (facultatif) wait pour le deuxième enfant aussi.

Sur read

Le read L'appel système n'ajoute pas de valeur nulle de fin, donc en général, vous devez regarder la valeur de retour, qui vous indique le nombre d'octets réellement lus.Définissez ensuite le caractère suivant sur null pour obtenir une chaîne C, ou utilisez le constructeur de plage pour std::string.

Sur les tuyaux

En ce moment, vous utilisez waitpid attendre que le processus enfant soit déjà terminé, alors lecture des tuyaux.Le problème est que si le processus enfant produit beaucoup de résultats, alors il bloc parce que le tube est plein et que le processus parent ne le lit pas.Le résultat sera une impasse, car l'enfant attend que le parent lise et que le parent attend que l'enfant termine.

Ce que vous devriez faire, c'est utiliser select attendre que les informations arrivent sur le côté de l'enfant stdout ou celui de l'enfant stderr.Lorsque l’entrée arrive, lisez-la ;cela permettra à l'enfant de continuer.Lorsque le processus enfant mourra, vous le saurez car vous obtiendrez la fin du fichier sur les deux. Alors vous pouvez appeler en toute sécurité wait ou waitpid.

Autres conseils

Le exec famille de fonctions remplace l’image de processus actuelle par une nouvelle image de processus.Lorsque vous exécutez,

execl("/bin/bash", "/bin/bash", "foo", NULL);

le code du processus en cours n'est plus exécuté.C'est pourquoi vous ne voyez jamais le résultat de l'exécution

execl("/bin/ls", "ls", "-1", (char *)0);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top