2012-06-09 13 views
7

Ich habe Schwierigkeiten, eine Shell mit Pipelines für die Klasse zu implementieren.UNIX-Pipes auf C-Block auf lesen

typedef struct { 
    char** cmd; 
    int in[2]; 
    int out[2]; 
} cmdio; 

cmdio cmds[MAX_PIPE + 1]; 

Befehle in der Pipeline werden in cmds gelesen und gespeichert.

cmdio[i].in ist das Paar von Dateideskriptoren der von pipe() zurückgegebenen Eingabe-Pipe. Für den ersten Befehl, der von der Terminaleingabe liest, ist es nur {fileno (stdin), -1}. cmdin[i].out ist für die Ausgabe Rohr/Terminal-Ausgang ähnlich. cmdio[i].in ist das gleiche wie cmd[i-1].out. Zum Beispiel:

$ ls -l | sort | wc 

CMD: ls -l 
IN: 0 -1 
OUT: 3 4 

CMD: sort 
IN: 3 4 
OUT: 5 6 

CMD: wc 
IN: 5 6 
OUT: -1 1 

Wir geben jeden Befehl zu process_command, die eine Reihe von Dingen tun:

for (cmdi = 0; cmds[cmdi].cmd != NULL; cmdi++) { 
    process_command(&cmds[cmdi]); 
} 

Jetzt, nach innen process_command:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Das Problem ist, dass die von der Lese Rohrblöcke für immer:

COMMAND $ ls | wc 
Created pipe, in: 5 out: 6 
Foreground pid: 9042, command: ls, Exited, info: 0 
[blocked running read() within wc] 

Wird anstelle des Prozesses mit execvp Austausch ich dies nur tun:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout)); 
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 

    char buf[6]; 
    read(fileno(stdin), buf, 5); 
    buf[5] = '\0'; 

    printf("%s\n", buf); 
    exit(0); 
} 

Es arbeiten passiert:

COMMAND $ cmd1 | cmd2 | cmd3 | cmd4 | cmd5 
Pipe creada, in: 11 out: 12 
Pipe creada, in: 13 out: 14 
Pipe creada, in: 15 out: 16 
Pipe creada, in: 17 out: 18 
hola! 
Foreground pid: 9251, command: cmd1, Exited, info: 0 
Foreground pid: 9252, command: cmd2, Exited, info: 0 
Foreground pid: 9253, command: cmd3, Exited, info: 0 
Foreground pid: 9254, command: cmd4, Exited, info: 0 
hola! 
Foreground pid: 9255, command: cmd5, Exited, info: 0 

Was könnte das Problem?

Antwort

4

Sie haben nicht genug schließt. Im Code:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Nachdem Sie die Rohre stdin und stdout (via fileno()) dupliziert haben, müssen Sie die Rohre schließen:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    close(cmd->in[0]); // Or your error checked version, but I'd use a function 
    close(cmd->out[1]);  
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Die Programme Finishing nicht, weil es Ein Schreibende der Datei ist noch offen. Vergessen Sie auch nicht, dass, wenn der übergeordnete Prozess (Shell) die Pipes erstellt, beide Enden der Pipe geschlossen werden müssen. Es ist wahrscheinlich der häufigste Fehler, wenn man nicht genug Rohre schließt, wenn man anfängt, Rohrleitungen zu lernen.

+0

Hum, funktioniert nicht: \ Es ist immer noch das gleiche, außer stdin/stdout wird geschlossen, bevor der Befehl ausgeführt wird, so zum Beispiel 'cat' scheitert mit' cat: stdin: Bad file descriptor'. Aber selbst wenn ich es vermeide, stdin/out zu schließen, ist das Verhalten das gleiche wie zuvor. Soweit ich verstehe, beziehen sich sowohl 'cmd -> (in | out) [(0 | 1)]' auf die gleichen zugrunde liegenden Dateien wie 'fileno (std (in | out)', nicht wahr? –

+0

Nun Jede Pipe hat einen Lesedeskriptor und einen Schreibdeskriptor.Wie ich es verstehe, haben Sie zwei Pipes zwischen Eltern und Kind (oder zwischen den beiden Kindern).In jedem Child-Prozess wollen Sie nur eine 'dup2()' des Lese-Endes einer Pipe auf 'stdin' öffnen und nur die' dup2() 'des Schreib-Endes der anderen Pipe auf' stdout' öffnen; alle vier der ursprünglichen Enden der Pfeife sollten geschlossen sein (was Leute überrascht). Ich bin mir nicht sicher, ob wir genug vom Code haben, um zu sagen, was sonst noch schief läuft. –

+0

Dank Jonathan, ich habe nicht beide Enden der Rohre von den Eltern geschlossen, wie du gesagt hast. Danke vielmals! –

1

Alles klar, ich habe es endlich gelöst.

Auf dem übergeordneten Prozess, kurz nach der ganzen Kind Gabel, lege ich:

f (cmd->in[0] != fileno(stdin)) { 
    close(cmd->in[0]); 
    close(cmd->in[1]); 
} 

Und voilà. Ich hatte so etwas schon einmal gemacht, aber ich habe mich vertippt und stattdessen close(cmd->out[0]); gemacht. So, das war es. Es macht jetzt Sinn.