2010-08-19 34 views
6

Cơ sở: Viết chương trình để truy vấn người dùng cho hai chuỗi đầu vào. Mỗi chuỗi đầu vào phải là lệnh unix, với các đối số được cho phép. Ví dụ: đầu vào 1 có thể là ls -l và đầu vào 2 có thể là more. Chương trình sau đó sẽ tạo một đường ống và hai tiến trình con. Quá trình con đầu tiên sẽ chạy lệnh được chỉ định trong đầu vào đầu tiên. Nó sẽ xuất ra ống thay vì đầu ra tiêu chuẩn. Quá trình con thứ hai sẽ chạy lệnh được chỉ định trong đầu vào thứ hai. Nó sẽ lấy đầu vào của nó từ đường ống chứ không phải đầu vào tiêu chuẩn. Quá trình cha mẹ sẽ chờ đợi trên hai đứa con của mình để hoàn thành, sau đó toàn bộ điều sẽ lặp lại. Việc thực hiện sẽ dừng khi biểu tượng '@' được nhập làm lệnh đầu tiên. Đây là mã tôi có:Đường ống và quy trình

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

int main(){ 

    /* Program Termination Symbol */ 
    const char terminate = '@'; 

    /* String delimiter */ 
    const char delimiter = ' '; 

    /* Pipe file ID's */ 
    int fileID[2]; 

    /* Parent ID's */ 
    int pid1, pid2; 

    /* String token */ 
    char * token, * token2; 

    /* User input */ 
    char * user_input, line[100]; 

    user_input = (char *) malloc(100); 

    /* Unix Commands */ 
    char * command1[10], *command2[10]; 

    for (int i=0; i<10; i++) 
    { 
    command1[i] = (char *)malloc(100*sizeof(char)); 
    command2[i] = (char *)malloc(100*sizeof(char)); 
    } 

    /* Begin main program logic */ 

    printf("Please enter the first command: \n"); 

    user_input = gets(line); 

    while (user_input[0] != terminate) 
    { 
    token = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
     { 
     if (i == 0) 
     { 
     token = strtok(user_input, &delimiter); 
     } else { 
     token = strtok(NULL, &delimiter); 
     } 

     if (token != NULL) 
     { 
     strcpy(command1[i], token); 
     } else { 
     command1[i] = 0; 
     } 
     } 

    printf("Please enter the second command: \n"); 
    user_input = gets(line); 

    token2 = (char *) malloc(100*sizeof(char)); 

    for (int i=0; i<10; i++) 
    { 
     if (i == 0) 
     { 
     token2 = strtok(user_input, &delimiter); 
     } else { 
     token2 = strtok(NULL, &delimiter); 
     } 

     if (token2 != NULL) 
     { 
     strcpy(command2[i], token2); 
     } else { 
     command2[i] = 0; 
     } 
    } 


    /* Pipe and execute user commands */ 

    /* Create pipe */ 
    pipe(fileID); 

    /* Create child processes */ 

    pid1 = fork(); 

    if (pid1 != 0) 
    { 
     pid2 = fork(); 
    } 

    /* First child process */ 
    if (pid1 == 0) 
    { 
     dup2(fileID[1], 1); 
     execvp(command1[0], command1); 
    } 

    /* Second child process */ 
    if (pid2 == 0) 
    { 
     dup2(fileID[0], 0); 
     execvp(command2[0], command2); 
    } 

    /* Wait for children to terminate */ 
    wait(&pid1); 
    wait(&pid2); 

    /* Repeat */ 
     printf("Please enter the first command: \n"); 
    user_input = gets(line); 
    } 

    return 0; 
} 

Vấn đề tôi đang gặp phải là do sự chờ đợi của mình. Nếu tôi có cả hai, điều đó có ý nghĩa với tôi (một lần chờ đợi cho mỗi đứa trẻ) thì chương trình bị đóng băng sau khi thực hiện đường ống đầu tiên. Nếu tôi loại bỏ sự chờ đợi thứ hai, sau đó chương trình sẽ bắt đầu vòng lặp của nó một lần nữa, nhưng sẽ không chấp nhận đầu vào bàn phím khác hơn là nhập, và sẽ tạo ra một segfault. Vì vậy, với cả thời gian chờ, đầu vào và đầu ra là ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 

... và sau đó nó khóa lại. Nếu tôi xóa lần chờ thứ hai, đầu vào/đầu ra là ...

Please enter the first command: 
ls 
Please enter the second command: 
more 
Pipe 
Pipe.c 
Pipe.c~ 
Please enter the first command: 
(I hit enter, nothing else will work) 
Segmentation fault 

Bất cứ ai có bất cứ đề xuất nào? Nó rõ ràng liên quan đến việc chờ đợi trên hai quy trình, nhưng tôi đang thua lỗ như thế nào để xử lý nó.


Chương trình này hiện đang hoạt động 100% - cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn, tất cả mọi người! Stack overflow là một trong những tài nguyên tốt nhất trên internet. Cảm ơn tất cả các bạn rất nhiều vì đã dành thời gian để xem xét mã của tôi và cho tôi đề xuất của bạn.

+0

Bạn chỉ cần đợi trẻ ở cuối đường ống. –

+0

@PeterRitchie Xin lưu ý rằng thẻ bài tập về nhà đã không được chấp nhận (đọc phần mô tả). Cảm ơn. – Tim

Trả lời

5

Tôi đồng ý với mọi thứ torak đã nói, nhưng để giải quyết vấn đề của bạn, bạn cần phải đóng ống. Tôi nghĩ bạn đang "treo" vì ống vẫn mở.

Vì vậy, trong bố mẹ, ngay trước khi "chờ", tôi sẽ đóng các đường ống.

close(fileID[0]); 
close(fileID[1]); 
wait(&pid_status); 
wait(&pid_status); 

Sau đó, ngay trước mỗi execvp, tôi sẽ đóng đầu của ống đứa trẻ sẽ không được sử dụng:

close(fileID[0]); 
dup2(fileID[1], 1); 
execvp(command1[0], command1); 


close(fileID[1]); 
dup2(fileID[0], 0); 
execvp(command2[0], command2); 

Điều đó sẽ giải quyết treo của bạn. Ngoài các đề xuất được thực hiện bởi torak, tôi cũng sẽ khuyên bạn nên thay vì để ngăn chặn tràn bộ đệm.

+0

Điều đó giải quyết việc treo, cảm ơn bạn rất nhiều. Chương trình bây giờ hoạt động (chủ yếu) một cách chính xác. Nhiệm vụ này đã khiến tôi phát điên. Chúng tôi đã dành tuần cuối cùng để thảo luận về quản lý bộ nhớ, vì vậy tôi đã tin rằng nhiệm vụ lập trình của chúng tôi sẽ bao gồm điều đó ... nhưng, không - từ ngoài cánh đồng, đường ống. – rybosome

4

Một vài điều. Không chắc chắn rằng họ là nguyên nhân của vấn đề của bạn, nhưng vẫn còn những điều cần xem xét trước khi gửi bài tập về nhà của bạn.

  1. Tôi không nghĩ rằng bạn đang sử dụng wait chính xác. Theo http://linux.die.net/man/2/wait, nó không lấy con trỏ pid làm đối số.

  2. Mỗi lần vòng lặp, bạn gọi malloc cho tokentoken2, nhưng tôi không thấy bản phát hành bộ nhớ tương ứng.

  3. Bạn đã viết một hàm nguyên khối duy nhất. Thực hành mã hóa tốt sẽ đề nghị chia nhỏ ra thành một tập hợp các chương trình con

  4. Cuối cùng, và nó có thể liên quan đến điểm 3, hai dòng mã sau xuất hiện hai lần trong mã của bạn. Một lần nữa nó không phải là một lỗi, nhưng trùng lặp không cần thiết, và không thích hợp.

    printf ("Vui lòng nhập lệnh đầu tiên: \ n"); user_input = được (dòng);

+0

Có chờ đợi có trạng thái int * được gọi. Hãy xem xét sử dụng waitpid mà có pid, trạng thái và các tùy chọn như tranh luận. – Robb

+0

Tôi có lẽ nên biết bây giờ, nhưng ai đó có thể giải thích cho tôi tại sao mã ở cuối câu trả lời của tôi không được phát hành đúng. – torak

+0

bạn có nhớ thụt lề dòng không? –

1

Trước hết, bạn cũng đang gọi số wait từ quá trình con [chỉnh sửa: không, bạn không, vì mỗi đứa trẻ gọi execvp].

Ngoài ra, wait không lấy con trỏ đến pid của trẻ, nhưng đến một biến mà trạng thái của quá trình sẽ được ghi vào (có nghĩa là bạn đang vứt bỏ con của bạn).

Cuối cùng, hãy thử sử dụng waitpid bằng tùy chọn "WNOHANG". Nó sẽ không treo, bạn có thể đặt cả hai trên một vòng lặp trong khi bạn làm công cụ khác, và bạn có thể kiểm tra xem liệu các tiến trình con đã thoát bằng cách kiểm tra các biến trạng thái. man waitpid.

+0

Tôi tin rằng tôi thực sự không gọi chờ đợi từ các tiến trình con - khi câu lệnh execvp gặp phải, phần còn lại của mã trong tiến trình hiện tại không được thực thi. Nó được thay thế bằng mã lệnh từ cuộc gọi execvp. Ít nhất, đó là sự hiểu biết của tôi về nó. Đối với việc sử dụng pid như một giá trị được thông qua ... Tôi không quan tâm đến tình trạng được trả về từ chờ đợi, vì vậy tôi vượt qua nó một giá trị tôi không còn cần. Theo hiểu biết của tôi, chờ đợi sẽ chỉ đơn giản là chờ đợi trên một quá trình con duy nhất - không có cách nào để kiểm soát mà với chức năng đó. – rybosome

+0

True, execvp dừng việc thực hiện của trẻ em. Tôi sẽ làm lại câu trả lời của tôi. Đối với pid, bạn có thể không cần nó ngay bây giờ, nhưng nó là * rất * hình thức xấu để tái sử dụng các biến cho các mục đích khác nhau như thế này, và cuối cùng bạn có thể thay đổi chương trình của bạn theo cách mà bạn cần pid sau này. Vì vậy, đặc biệt là cho một bài tập về nhà, bạn nên sử dụng các biến khác nhau cho tình trạng. Không có ý nghĩa trong việc cố gắng tiết kiệm với các tên biến. Và chức năng waitpid cho phép bạn xác định một pid cụ thể để chờ đợi. Hãy xem manpage của nó. – rbp

+0

Bạn đúng rằng đây là thực hành không tốt - tôi chỉ nên tạo một biến khác. Những ngày này, nó không giống như một biến int duy nhất là không gian khủng khiếp cần thiết mà nên được bảo quản bằng mọi giá. Tôi đã thay đổi chương trình của mình để làm điều này. Tôi đánh giá cao đề xuất của bạn! – rybosome

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