2012-02-13 3 views
7

以下の短いプログラムは、コマンドラインから渡されたargvを反復し、各引数をexecすることを意図しています。これは私の宿題ではなく、むしろ私が宿題をする準備としてやっていることです。Unixファイル記述子がC言語でどのように機能するのか理解していますか?

最初の引数は、STDINおよびSTDOUTからの入力を取得し、パイプに書き込みます。各繰り返しの最後に(最後のものを除く)、ファイルディスクリプタがスワップされるので、最後のexecによって書き込まれたパイプは、次のパイプから読み込まれます。

./a.out /bin/pwd /usr/bin/wc 

は、作業ディレクトリの長さだけを印刷するためにこのように私は、例えば、意図しています。コードは

#include <stdio.h>                
#include <unistd.h>                
#include <sys/types.h>               
#include <stdlib.h>                
#include <string.h>                

main(int argc, char * argv[]) {             

    int i; 
    int left[2], right[2], nbytes; /* arrays for file descriptors */ 

    /* pointers for swapping */ 
    int (* temp); 
    int (* leftPipe) = left;     
    int (* rightPipe) = right; 

    pid_t childpid;                
    char readbuffer[80];               

    /* for the first iteration, leftPipe is STDIN */ 
    leftPipe[0] = STDIN_FILENO; 
    leftPipe[1] = STDOUT_FILENO; 

    for (i = 1; i < argc; i++) {             

    /* reopen the right pipe (is this necessary?) */ 
    pipe(rightPipe);                
    fprintf(stderr, "%d: %s\n", i, argv[i]); 
    fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);                      
    if ((childpid = fork()) == -1) {            
     perror("fork");               
     exit(1);                 
    }                   

    if (childpid == 0) {               

     /* read input from the left */            
     close(leftPipe[1]); /* close output */          
     dup2(leftPipe[0], STDIN_FILENO);           
     close(leftPipe[0]); /* is this necessary? A tutorial seemed to be doing this */ 

     /* write output to the right */           
     close(rightPipe[0]); /* close input */          
     dup2(rightPipe[1], STDOUT_FILENO);           
     close(rightPipe[1]);              

     execl(argv[i], argv[i], NULL);            
     exit(0);                 
    }                   

    wait();                  

    /* on all but the last iteration, swap the pipes */ 
    if (i + 1 < argc) {    

     /* swap the pipes */              
     fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]); 
     temp = leftPipe;               
     leftPipe = rightPipe;              
     rightPipe = temp;               
     fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]); 
    }                   
    }                    

    /* read what was last written to the right pipe */       
    close(rightPipe[1]); /* the receiving process closes 1 */     

    nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer));  
    readbuffer[nbytes] = 0; 
    fprintf(stderr, "Received string: %s\n", readbuffer);         

    return 0;                  
} 

UPDATEを次の:私はもともと/ binに/トイレ使用されるがしていた以下の試験例すべては、私が思ったところ水のクローゼットが全くないことをreveiled WCで。私は結果を修正する過程にある。

些細な場合の出力(./a.out /ビン/ PWD)は予想通りである:

1: /bin/pwd 
Received string: /home/zeigfreid/Works/programmatical/Langara/spring_2012/OS/labs/lab02/play 

最初の例(./a.out /ビンにこのプログラムを実行してから出力/ pwd/usr/bin/wc):

1: /bin/pwd 
0 1 3 4 
3 4 0 1 
2: /bin/wc 

この時点で、端末がハングアップしています(入力待ちの可能性があります)。

ご覧のとおり、文字列は受信されていません。私が想像していることは、上記の何かを間違って実行したことです。ポインタを交換したり、UNIXファイル記述子を理解できないことがあります。私の割り当ては、最終的には、任意に長いパイプを解釈することになります。これは私が問題を解決するために持っていたアイデアの1つです。私は木を吠える正しい道にいるかどうかを判断するのが難しい。私はUNIXファイル記述子を理解していますか?

UPDATE:

1: /bin/pwd 
0 1 3 4 
0 1 3 4 
3 4 0 1 
2: /bin/ls 
3 4 5 6 
Received string: a.out 
log 
pipe2.c 
play.c 
@ 

があります:第二引数として/ binは/ LSとそれを実行する

は、私は次の結果(数字は様々なポイントでのファイル記述子である)を得ましたそこにはまだゴミが残っていますが、今私はポインタを理解できないと心配しています!これらの2つのコマンドは互いに独立していますが、実際にはパイプを使用しません。

更新日:ガベージ文字は、文字列を閉じていないことが原因でした。今私はそれを閉じ、ごみはありません。

+0

を私は 'すべてのあなたの'のprintf(...)を変更することをお勧めしたいと思います 'fprintfの(stderrのに呼び出します、...) '。低レベルのルーチンと標準IO( 'のprintf(3)')を混合する( 'パイプ(2)' 'dup2のは、(2)' '近い(2)')それは価値があるよりも多くの問題です。 – sarnold

+0

Dulyが注目しました!私は副子が同意すると思います。 – Ziggy

+0

あなたはそれを印刷する前に、文字列を終了していない、これはごみを説明しています。 'read'の後に' readbytes [nbytes] = 0'を試してください。 –

答えて

2

「右」パイプの書き込み終了が、フォーク後のメインプロセスで正しく閉じられていないことが原因です。このため、wcは読書を止めることはありません(結局のところ、メインプロセスはパイプに書き込むことができます)。書き込み終了のファイルディスクリプタのすべてのコピーが閉じられた後でのみ、読み取りを停止します。ここで

は、固定されたバージョンです:

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <stdlib.h> 
#include <string.h> 

int main(int argc, char * argv[]) 
{ 
    int i; 
    int left[2], right[2], nbytes; /* arrays for file descriptors */ 

    /* pointers for swapping */ 
    int (* temp); 
    int (* leftPipe) = left; 
    int (* rightPipe) = right; 

    pid_t childpid; 
    char readbuffer[80]; 

    leftPipe[0] = STDIN_FILENO; 
    // no need to assign leftPipe[1] here, it will not be used 

    for (i = 1; i < argc; i++) { 
    pipe(rightPipe); // create new pipe 

    fprintf(stderr, "%d: %s\n", i, argv[i]); 
    fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]); 
    if ((childpid = fork()) == -1) { 
     perror("fork"); 
     exit(1); 
    } 

    if (childpid == 0) { 
     // use the reading end of the left pipe as STDIN 
     dup2(leftPipe[0], STDIN_FILENO); 
     // use the writing end of the right pipe as STDOUT 
     dup2(rightPipe[1], STDOUT_FILENO); 
     // close reading end of the right pipe 
     close(rightPipe[0]); 
     execl(argv[i], argv[i], NULL); 
     exit(0); 
    } 
    // IMPORTANT!! close writing end of the right pipe, otherwise 
    // the program will hang (this is the main bug in your original 
    // implementation) 
    close(rightPipe[1]); 

    // wait properly! 
    waitpid(childpid, NULL, 0); 

    /* on all but the last iteration, swap */ 
    if (i + 1 < argc) { 
     fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]); 
     temp = leftPipe; 
     leftPipe = rightPipe; 
     rightPipe = temp; 
     fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]); 
    } 
    } 

    nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer)); 
    readbuffer[nbytes] = 0; 
    fprintf(stderr, "Received string: %s\n", readbuffer); 

    return 0; 
} 

出力:

>> ./a.out /bin/ls /bin/cat /usr/bin/wc 
1: /bin/ls 
0 32767 3 4 
0 32767 3 4 
3 4 0 32767 
2: /bin/cat 
3 4 4 5 
3 4 4 5 
4 5 3 4 
3: /usr/bin/wc 
4 5 5 6 
Received string:  266  294 4280 

あなたはこのソリューションについての具体的な質問を得た場合、私に知らせてください:)存在しても、他のいくつかのマイナーな問題あなたの元のコード:

  • ポインタを使用しては不要です
  • intあなたは-Wallフラグを指定してコンパイル時に提示されるすべての警告を解決しませんでしたsize_t
  • の代わりに使用されます。(問題になることはありません確実にパフォーマンスが)、私たちは、パイプの周りにコピーすることができます

あなたが興味を持っている場合、これは私がそれを書かれているだろうかです:

#include <stdio.h> 
#include <unistd.h> 
#include <sys/wait.h> 
#include <stdlib.h> 
#include <string.h> 

int main(int argc, char **argv) { 
    size_t i, nbytes; 
    int left[2], right[2], tmp[2]; 
    pid_t childpid; 
    char readbuffer[80]; 

    left[0] = STDIN_FILENO; 

    for (i = 1; i < argc; ++i) { 
    pipe(right); 

    switch ((childpid = fork())) { 
     case -1: 
     perror("fork"); 
     exit(1); 
     case 0: 
     dup2(left[0], STDIN_FILENO); 
     dup2(right[1], STDOUT_FILENO); 
     close(right[0]); 
     execl(argv[i], argv[i], NULL); 
     default: 
     close(right[1]); 
     waitpid(childpid, NULL, 0); 
    } 

    if (i == argc - 1) break; 
    memcpy(tmp, left, sizeof tmp); 
    memcpy(left, right, sizeof left); 
    memcpy(right, tmp, sizeof right); 
    } 

    nbytes = read(right[0], readbuffer, sizeof readbuffer); 
    readbuffer[nbytes] = 0; 
    fprintf(stderr, "Received string: %s\n", readbuffer); 

    return 0; 
} 
+0

ニース!私はコンパイルしていませんでした - 壁、あなたは正しいです。通常、私はやる、と私はまた、スプリント-weak警告を修正するが、これは私はのように徹底されていなかっただけで実験しました。その答えを見るのがうれしかったのは、私が恐れていたような範疇的なエラーではなく、比較的小さいものでした。だから答えは "はい"ですが、私は詳細でもっと練習が必要です。ありがとう、あなたのソリューションは非常にいいです! – Ziggy

+0

@Ziggy:それがあなたを助けたら、あなたはこの答えを受け入れるように誘われています:) –

+0

私は確かにそうです!私は傾向がある:) – Ziggy

0

出力の最後にガベージを修正するには、最後のprintfの前に次の行を追加します。

readbuffer[nbytes] = 0; 

懸垂の問題について - 私はそれを修正するためにもう少し考えが必要です。私はそれが配管と緩衝と関係があると推測しています。

関連する問題