Věřím, že problém je v tom, že čekáte a uzavíráte se uvnitř stejné smyčky, která vytváří děti. Při první iteraci se podřízený program spustí (což zničí podřízený program a přepíše ho vaším prvním příkazem) a potom rodič zavře všechny své deskriptory souborů a čeká, až dítě skončí, než se iteruje k vytvoření dalšího potomka. . V tomto okamžiku, protože rodič uzavřel všechny své kanály, žádné další děti nebudou mít do čeho psát ani z čeho číst. Protože nekontrolujete úspěšnost svých volání dup2, zůstává to bez povšimnutí.
Chcete-li zachovat stejnou strukturu smyčky, musíte se ujistit, že nadřazený objekt zavírá pouze popisovače souborů, které již byly použity, ale ponechává ty, které nejsou, samotné. Poté, co budou všechny děti vytvořeny, může váš rodič čekat.
UPRAVIT :Ve své odpovědi jsem pomíchal rodič/dítě, ale zdůvodnění stále platí:proces, který pokračuje k rozvětvení, opět uzavře všechny své kopie rour, takže žádný proces po prvním rozvětvení nebude mít platné deskriptory souborů ke čtení do/psát z.
pseudo kód pomocí pole kanálů vytvořených předem:
/* 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] );
}
V tomto kódu původní nadřazený proces vytvoří pro každý příkaz potomka, a proto přežije celé utrpení. Děti zkontrolují, zda by měly dostat svůj vstup z předchozího příkazu a zda by měly poslat svůj výstup dalšímu příkazu. Potom zavřou všechny své kopie deskriptorů souborů roura a poté exec. Rodič nedělá nic jiného než větvení, dokud pro každý příkaz nevytvoří potomka. Poté zavře všechny své kopie deskriptorů a může čekat.
Vytvoření všech kanálů, které potřebujete jako první, a jejich správa ve smyčce je složité a vyžaduje určitou aritmetiku pole. Cíl však vypadá takto:
cmd0 cmd1 cmd2 cmd3 cmd4
pipe0 pipe1 pipe2 pipe3
[0,1] [2,3] [4,5] [6,7]
Když si uvědomíte, že v daném okamžiku potřebujete pouze dvě sady rour (rouru k předchozímu příkazu a roura k dalšímu příkazu), váš kód se zjednoduší a bude o něco robustnější. Ephemien zde uvádí pseudokód. Jeho kód je čistší, protože rodič a podřízený prvek nemusejí zbytečně cyklicky zavírat nepotřebné deskriptory souborů a protože rodič může snadno zavřít své kopie deskriptorů souborů ihned po rozvětvení.
Jako vedlejší poznámka:vždy byste měli zkontrolovat návratové hodnoty potrubí, dup2, fork a exec.
ÚPRAVA 2 :překlep v pseudo kódu. OP:num-pipes by byl počet trubek. Např. "ls | grep foo | sort -r" bude mít 2 kanály.
Zde je správný funkční kód
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);
}
Příslušný (zkrácený) kód je:
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);
}
Což znamená, že nadřazený (řídící) proces dělá toto:
- rozvětvení
- zavřete všechny kanály
- počkejte na proces dítěte
- další smyčka / podřízený prvek
Ale mělo by to být něco takového:
- rozvětvení
- rozvětvení
- rozvětvení
- zavřete všechny kanály (vše by mělo být nyní podvedeno)
- počkej na děti