2017-05-17 7 views
0

私はオープンコースからCシェルの実装を試していますが、出力バッファリングの動作について興味深いものがあります。C言語の奇妙な出力バッファリング動作

コードは、この(私は使用線PID = waitpidを(-1、& R、WNOHANG)注意)のような行く:RUNCMD関数は次のようである

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); 
} 

を(内ことに注意私は2つのプロセスを作成し、それらが終了するのを待ち)を取り扱うパイプ:「LSを|ソート」

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); 
} 

奇妙な事は、私が実行したときに、あるターミナルで、私は常にFを取得します出力

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

をollowingこれは、次のコマンドプロンプト「6828は$」印刷される前に、子プロセスからの出力は、まだ端末にフラッシュされていないことを示しています。しかし

、私はPID = waitpidを(-1、& R、WNOHANG))を使用してPID = waitpidを(-1、& R、0))(または待機を使用していない場合は())、出力は次のように通常のようになります。

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

私は長い間、問題の原因について考えてきたが、考えられる理由を思い付くしませんでした。誰かが何らかの理由を示唆できますか?

ありがとうございます!

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

whileループブレークすぐwaitpid場合は-1を返し、エラーの場合に返されます正確に何である:

答えて

1

は、このコードは、明確に定義された動作はありません。したがって、ループの本体が入力された場合、waitpidは、負ではない値を返します。0 - 子がまだ実行中であることを示すか、または終了した子のPIDです。これらはエラー条件ではないため、errnoの値は意味がありません。 ECHILDである可能性があります。その場合、ループが正しく破損します。

は値が意味のある場合はerrnoの値をチェックする必要があります。または、Posix standardを引用し、より正確には:

errnoの値は、明示的に設定すると、それは次の関数の呼び出しによって変更されるまで記載されている関数の呼び出し後に定義されなければなりませんアプリケーションが値を割り当てるかどうかを指定します。 errnoの値は、関数の戻り値によって有効であることが示されている場合にのみ検査する必要があります。

しかし、なぜ私はあなたが忙しいループの必要性を感じるのかと疑問に思っているWNOHANGを使用しています。子供が実際に終了するまで、親プロセスがシステムコールを繰り返し実行するので、これは大量のリソースを浪費します。子が終了するまで待つつもりなので、waitを呼び出すか、またはにフラグ値として0を指定するほうがはるかに意味があります。

errnoを-1とし、EINTRに設定した場合は、wait(またはwaitpid)を繰り返してもかまいません。 -1errnoが返されてもEINTRでもECHILDでもない場合は、ログに記録する可能性のあるハードエラーが発生しています。しかし、それはあなたの問題、afaicsに関連していません。