GNU/Linux >> Linux Esercitazione >  >> Linux

Implementazione di più pipe in C

Credo che il problema qui sia che tu aspetti e ti chiudi all'interno dello stesso ciclo che sta creando i bambini. Alla prima iterazione, il figlio verrà eseguito (che distruggerà il programma figlio, sovrascrivendolo con il tuo primo comando) e quindi il genitore chiude tutti i suoi descrittori di file e attende che il figlio finisca prima di iterare per creare il figlio successivo . A quel punto, poiché il genitore ha chiuso tutte le sue pipe, gli altri figli non avranno nulla su cui scrivere o da cui leggere. Dal momento che non stai verificando il successo delle tue chiamate dup2, questo passerà inosservato.

Se vuoi mantenere la stessa struttura del ciclo, dovrai assicurarti che il genitore chiuda solo i descrittori di file che sono già stati usati, ma lasci quelli che non lo sono stati da soli. Quindi, dopo che tutti i figli sono stati creati, il tuo genitore può aspettare.

MODIFICA :ho confuso genitore/figlio nella mia risposta, ma il ragionamento è ancora valido:il processo che prosegue con il fork chiude nuovamente tutte le sue copie delle pipe, quindi qualsiasi processo dopo il primo fork non avrà descrittori di file validi da leggere da/scrivere da.

pseudo codice, utilizzando un array di pipe creato in anticipo:

/* parent creates all needed pipes at the start */
for( i = 0; i < num-pipes; i++ ){
    if( pipe(pipefds + i*2) < 0 ){
        perror and exit
    }
}

commandc = 0
while( command ){
    pid = fork()
    if( pid == 0 ){
        /* child gets input from the previous command,
            if it's not the first command */
        if( not first command ){
            if( dup2(pipefds[(commandc-1)*2], 0) < ){
                perror and exit
            }
        }
        /* child outputs to next command, if it's not
            the last command */
        if( not last command ){
            if( dup2(pipefds[commandc*2+1], 1) < 0 ){
                perror and exit
            }
        }
        close all pipe-fds
        execvp
        perror and exit
    } else if( pid < 0 ){
        perror and exit
    }
    cmd = cmd->next
    commandc++
}

/* parent closes all of its copies at the end */
for( i = 0; i < 2 * num-pipes; i++ ){
    close( pipefds[i] );
}

In questo codice, il processo genitore originale crea un figlio per ogni comando e quindi sopravvive all'intero calvario. I bambini controllano se devono ricevere il loro input dal comando precedente e se devono inviare il loro output al comando successivo. Quindi chiudono tutte le loro copie dei descrittori di file pipe e quindi exec. Il genitore non fa altro che eseguire il fork fino a quando non viene creato un figlio per ogni comando. Quindi chiude tutte le sue copie dei descrittori e può continuare ad attendere.

Creare prima tutte le pipe di cui hai bisogno e poi gestirle nel ciclo è complicato e richiede un po 'di aritmetica dell'array. L'obiettivo, tuttavia, è simile a questo:

cmd0    cmd1   cmd2   cmd3   cmd4
   pipe0   pipe1  pipe2  pipe3
   [0,1]   [2,3]  [4,5]  [6,7]

Comprendere che, in un dato momento, hai solo bisogno di due serie di pipe (la pipe per il comando precedente e la pipe per il comando successivo) semplificherà il tuo codice e lo renderà un po' più robusto. Ephemient fornisce uno pseudo-codice per questo qui. Il suo codice è più pulito, perché il genitore e il figlio non devono eseguire cicli inutili per chiudere i descrittori di file non necessari e perché il genitore può facilmente chiudere le sue copie dei descrittori di file immediatamente dopo il fork.

Come nota a margine:dovresti sempre controllare i valori restituiti da pipe, dup2, fork ed exec.

MODIFICA 2 :errore di battitura in pseudo codice. OP:num-pipe sarebbe il numero di pipe. Ad esempio, "ls | grep foo | sort -r" avrebbe 2 pipe.


Ecco il codice di funzionamento corretto

void runPipedCommands(cmdLine* command, char* userInput) {
    int numPipes = countPipes(userInput);


    int status;
    int i = 0;
    pid_t pid;

    int pipefds[2*numPipes];

    for(i = 0; i < (numPipes); i++){
        if(pipe(pipefds + i*2) < 0) {
            perror("couldn't pipe");
            exit(EXIT_FAILURE);
        }
    }


    int j = 0;
    while(command) {
        pid = fork();
        if(pid == 0) {

            //if not last command
            if(command->next){
                if(dup2(pipefds[j + 1], 1) < 0){
                    perror("dup2");
                    exit(EXIT_FAILURE);
                }
            }

            //if not first command&& j!= 2*numPipes
            if(j != 0 ){
                if(dup2(pipefds[j-2], 0) < 0){
                    perror(" dup2");///j-2 0 j+1 1
                    exit(EXIT_FAILURE);

                }
            }


            for(i = 0; i < 2*numPipes; i++){
                    close(pipefds[i]);
            }

            if( execvp(*command->arguments, command->arguments) < 0 ){
                    perror(*command->arguments);
                    exit(EXIT_FAILURE);
            }
        } else if(pid < 0){
            perror("error");
            exit(EXIT_FAILURE);
        }

        command = command->next;
        j+=2;
    }
    /**Parent closes the pipes and wait for children*/

    for(i = 0; i < 2 * numPipes; i++){
        close(pipefds[i]);
    }

    for(i = 0; i < numPipes + 1; i++)
        wait(&status);
}

Il codice pertinente (abbreviato) è:

    if(fork() == 0){
            // do child stuff here
            ....
    }
    else{
            // do parent stuff here
            if(command != NULL)
                command = command->next;

            j += 2;
            for(i = 0; i < (numPipes ); i++){
               close(pipefds[i]);
            }
           while(waitpid(0,0,0) < 0);
    }

Il che significa che il processo padre (di controllo) fa questo:

  • forchetta
  • chiudi tutti i tubi
  • attendere il processo figlio
  • ciclo successivo / figlio

Ma dovrebbe essere qualcosa del genere:

  • forchetta
  • forchetta
  • forchetta
  • chiudi tutte le pipe (tutto dovrebbe essere stato ingannato ora)
  • aspetta i bambini

Linux
  1. Come aggiungere più host in phpMyAdmin

  2. Come comprimere più file su Linux

  3. Come inviare Stdout a più comandi?

  4. Argomenti multipli in Shebang?

  5. Più librerie glibc su un singolo host

implementazione dell'operatore sizeof

Esegui più script Python contemporaneamente

Pipe, dup2 e exec()

Taglia le pagine di un PDF in più pagine

systemd - Dando al mio servizio più argomenti

Tail più file remoti