2012-02-13 32 views
7

Chương trình ngắn dưới đây nhằm lặp qua argv truyền từ dòng lệnh và thực hiện từng đối số. Đây không phải là bài tập về nhà của tôi, mà đúng hơn là tôi đang chuẩn bị làm bài tập ở nhà.Tôi có hiểu cách các trình mô tả tập tin Unix hoạt động trong C không?

Đối số đầu tiên được nhập từ STDIN và STDOUT và ghi vào một đường ống. Vào cuối mỗi lần lặp (ngoại trừ lần cuối), các bộ mô tả tập tin được hoán đổi, sao cho đường dẫn được viết bởi lệnh cuối cùng sẽ được đọc từ lần kế tiếp. Theo cách này, tôi có ý định, ví dụ: đối với

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

để chỉ in chiều dài của thư mục làm việc. Mã sau

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

CẬP NHẬT: trong tất cả các trường hợp thử nghiệm dưới đây Ban đầu tôi đã sử dụng/bin/wc nhưng mà WC reveiled rằng closet nước không phải là ở tất cả các nơi mà tôi nghĩ. Tôi đang trong quá trình sửa đổi kết quả.

Sản lượng trong một vụ án tầm thường (./a.out/bin/pwd) là như mong đợi:

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

Kết quả chạy chương trình này với ví dụ đầu tiên (./a.out/bin/pwd/usr/bin/wc):

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

Tại thời điểm đó, thiết bị đầu cuối bị treo (có thể đang chờ nhập).

Như bạn thấy, chuỗi không được nhận. Điều tôi tưởng tượng là tôi đã làm điều gì đó sai ở trên, hoặc khi hoán đổi con trỏ, hoặc tôi không hiểu các mô tả tập tin unix. Nhiệm vụ của tôi, cuối cùng, sẽ là để giải thích các đường ống dài tùy tiện, và đây là một trong những ý tưởng tôi có để giải quyết vấn đề. Tôi đang gặp khó khăn khi đánh giá liệu tôi có đang đi đúng hướng để sủa cây không. Tôi có hiểu các mô tả tập tin unix không?

UPDATE:

Chạy nó với/bin/ls như là đối số thứ hai, tôi nhận được kết quả như sau (các con số là file descriptor tại các điểm khác nhau):

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 
@ 

Có vẫn còn một số rác cuối cùng ở đó, nhưng bây giờ tôi quan tâm nhiều hơn là tôi không hiểu con trỏ! Hai lệnh này độc lập với nhau mặc dù chúng không thực sự sử dụng đường ống.

CẬP NHẬT: ký tự rác từ không đóng chuỗi. Bây giờ tôi đóng nó, và không có rác.

+0

tôi muốn đề nghị thay đổi tất cả 'printf của bạn (...) 'gọi vào' fprintf (stderr , ...) '. Trộn chuẩn IO ('printf (3)') với các thường trình mức thấp hơn ('pipe (2)', 'dup2 (2)', 'close (2)') là rắc rối nhiều hơn giá trị. – sarnold

+0

Lưu ý đúng! Tôi tưởng tượng nẹp sẽ đồng ý. – Ziggy

+0

Bạn không chấm dứt chuỗi trước khi in, điều này giải thích rác. Hãy thử 'readbytes [nbytes] = 0' sau' read'. –

Trả lời

2

Việc treo được gây ra bởi thực tế là đầu viết của ống "đúng" không được đóng đúng cách trong quy trình chính sau khi tắt. Bởi vì điều này, wc sẽ không bao giờ ngừng đọc (sau khi tất cả, quá trình chính vẫn có thể viết công cụ vào đường ống!).Nó chỉ dừng đọc sau khi tất cả các bản sao của bộ mô tả tập tin của đầu ghi đã bị đóng.

Đây là một phiên bản cố định:

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

Output:

>> ./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 

Nếu bạn có câu hỏi cụ thể về giải pháp này, xin vui lòng cho tôi biết :) Ngoài ra còn một số vấn đề nhỏ khác với mã ban đầu của bạn:

  • sử dụng con trỏ là không cần thiết , Chúng tôi chỉ có thể sao chép xung quanh các đường ống (hiệu suất chắc chắn sẽ không phải là một vấn đề;)
  • int được sử dụng thay vì size_t
  • bạn không khắc phục được tất cả các cảnh báo rằng sẽ được trình bày cho bạn khi biên dịch với -Wall cờ

Nếu bạn quan tâm, đây là thế nào tôi sẽ có văn bản nó:

#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

Đẹp! Tôi không biên dịch với -Wall, bạn nói đúng. Bình thường, tôi cũng vậy, và tôi cũng sửa chữa những lời cảnh báo nhảm nhí, nhưng đây chỉ là một thử nghiệm vì vậy tôi đã không được triệt để. Vui mừng khi thấy câu trả lời là một cái gì đó tương đối nhỏ, chứ không phải là một lỗi phân loại như tôi đã lo sợ. Vì vậy, câu trả lời là "có" nhưng tôi cần thực hành nhiều hơn với các chi tiết. Cảm ơn rất nhiều giải pháp của bạn là rất tốt đẹp! – Ziggy

+0

@ Ziggy: Nếu nó giúp bạn, bạn được mời chấp nhận câu trả lời này :) –

+0

Tôi chắc chắn sẽ làm điều đó! Tôi có xu hướng :) – Ziggy

0

Để khắc phục sự cố ở cuối đầu ra, hãy thêm dòng sau trước số printf cuối cùng.

readbuffer[nbytes] = 0; 

Về vấn đề treo - tôi cần suy nghĩ thêm một chút để khắc phục sự cố đó. Tôi đoán nó là một cái gì đó để làm với hệ thống ống nước và đệm.

Các vấn đề liên quan