2017-05-17 3 views
0

Ich probiere eine C-Shell-Implementierung aus einem offenen Kurs aus, doch es gibt etwas Faszinierendes am Verhalten der Ausgabepufferung.Seltsames Ausgabepufferverhalten in C

Der Code lautet wie folgt (beachten Sie die Zeile, wo ich verwenden pid = waitpid (-1, & r, WNOHANG)):

int 
main(void) 
{ 
    static char buf[100]; 
    int fd, r; 
    pid_t pid = 0; 

    // Read and run input commands. 
    while(getcmd(buf, sizeof(buf)) >= 0){ 
    if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){ 
     buf[strlen(buf)-1] = 0; // chop \n 
     if(chdir(buf+3) < 0) 
     fprintf(stderr, "cannot cd %s\n", buf+3); 
     continue; 
    } 
    if((pid = fork1()) == 0) 
     runcmd(parsecmd(buf)); 
    while ((pid = waitpid(-1, &r, WNOHANG)) >= 0) { 
     if (errno == ECHILD) { 
      break; 
     } 
    } 
    } 
    exit(0); 
} 

Die runcmd Funktion wie folgt ist (das in beachten Rohrhandhabungs I erstellen 2 Prozesse Kind und warten, bis sie) beenden:

void 
runcmd(struct cmd *cmd) 
{ 
    int p[2], r; 
    struct execcmd *ecmd; 
    struct pipecmd *pcmd; 
    struct redircmd *rcmd; 

    if(cmd == 0) 
    exit(0); 

    switch(cmd->type){ 
    case ' ': 
    ecmd = (struct execcmd*)cmd; 
    if(ecmd->argv[0] == 0) { 
     exit(0); 
    } 
    // Your code here ... 
    // fprintf(stderr, "starting to run cmd: %s\n", ecmd->argv[0]); 
    execvp(ecmd->argv[0], ecmd->argv); 
    fprintf(stderr, "exec error !\n"); 
    exit(-1); 

    break; 

    case '>': 
    case '<': 
    rcmd = (struct redircmd*)cmd; 
    // fprintf(stderr, "starting to run <> cmd: %s\n", rcmd->file); 
    // Your code here ... 
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 
    if (rcmd->type == '<') { 
     // input 
     close(0); 
     if (open(rcmd->file, O_RDONLY, mode) != 0) { 
     fprintf(stderr, "Opening file error !\n"); 
     exit(-1); 
     } 
    } else { 
     // output 
     close(1); 
     if (open(rcmd->file, O_WRONLY|O_CREAT|O_TRUNC, mode) != 1) { 
     fprintf(stderr, "Opening file error !\n"); 
     exit(-1); 
     } 
    } 

    runcmd(rcmd->cmd); 
    break; 

    case '|': 
    pcmd = (struct pipecmd*)cmd; 
    // fprintf(stderr, "starting to run pcmd\n"); 
    // Your code here ... 
    pipe(p); 

    if (fork1() == 0) { 
     // child for read, right side command 
     close(0); 
     if (dup(p[0]) != 0) { 
     fprintf(stderr, "error when dup !\n"); 
     exit(-1); 
     } 
     close(p[0]); 
     close(p[1]); 
     runcmd(pcmd->right); 
     fprintf(stderr, "exec error !\n"); 
    } 
    if (fork1() == 0) { 
     // left side command for writing 
     close(1); 
     if (dup(p[1]) != 1) { 
     fprintf(stderr, "dup error !\n"); 
     exit(-1); 
     } 
     close(p[0]); 
     close(p[1]); 
     runcmd(pcmd->left); 
     fprintf(stderr, "exec error !\n"); 
    } 
    close(p[0]); 
    close(p[1]); 
    int stat; 
    wait(&stat); 
    wait(&stat); 

    break; 

    default: 
    fprintf(stderr, "unknown runcmd\n"); 
    exit(-1); 
    }  
    exit(0); 
} 

die wierd Sache ist, wenn ich "ls | Art" execute im Terminal, bekomme ich ständig die f Dieser Ausgang ach

6.828$ ls | sort 
6.828$ a.out 
sh.c 
t.sh 

zeigt an, dass, bevor die nächste Eingabeaufforderung „6828 $“ gedruckt wird, wird die Ausgabe von dem Kindprozess immer noch nicht mit dem Anschluß gespült.

Allerdings, wenn ich nicht verwenden pid = waitpid (-1, & r, WNOHANG)) und verwenden pid = waitpid (-1, & r, 0)) (oder wait()), wäre der Ausgang normal wie:

6.828$ ls | sort 
a.out 
sh.c 
t.sh 

ich habe für eine lange Zeit über die Ursache des Problems gedacht, aber mit einem möglichen Grunde nicht kommen. Kann jemand einen möglichen Grund vorschlagen?

Vielen Dank!

Antwort

1

Dieser Code muss nicht gut definierte Verhalten:

while ((pid = waitpid(-1, &r, WNOHANG)) >= 0) { 
    if (errno == ECHILD) { 
     break; 
    } 
} 

Die while Schleife bricht sofort, wenn waitpid -1 zurück, was genau das ist, was sie im Falle eines Fehlers zurück. Wenn also der Körper der Schleife eingegeben wird, gab waitpid einen nichtnegativen Wert zurück: Entweder 0 - was anzeigt, dass das Kind noch ausgeführt wird - oder die PID eines Kindes, das ausgegangen ist. Dies sind keine Fehlerbedingungen, daher ist der Wert errno nicht aussagekräftig. Es könnte ECHILD sein, in diesem Fall wird die Schleife falsch unterbrochen.

Sie müssen überprüfen Sie nur den Wert von errno in Fällen, in denen der Wert sinnvoll ist. Oder um genauer zu sein, die unter Angabe Posix standard:

Der Wert errno wird erst nach Aufruf eine Funktion definiert werden, für die sie explizit angegeben wird gesetzt werden und bis sie durch den nächsten Funktionsaufruf geändert werden oder wenn die Anwendung einen Wert zuweist. Der Wert errno sollte nur überprüft werden, wenn der Rückgabewert einer Funktion dies angibt.

Aber ich bin verwirrt, warum Sie es notwendig zu beschäftigt Schleife mit WNOHANG fühlen. Das ist eine massive Verschwendung von Ressourcen, da Ihr Elternprozess den Systemaufruf wiederholt ausführt, bis das Kind tatsächlich beendet wird.Da Sie wirklich warten wollen, bis das Kind beendet wird, wäre es viel sinnvoller, einfach wait oder 0 als Flag-Wert zu waitpid zu benennen.

Auf der anderen Seite, könnte man die wait (oder waitpid) wiederholt werden soll, wenn sie -1 zurück mit errno Set EINTR. Und wenn es -1 zurückgibt und errno ist weder EINTR noch ECHILD, dann ist ein schwerer Fehler aufgetreten, den Sie möglicherweise protokollieren möchten. Aber das hängt nicht mit deinem Problem zusammen, afaics.