2012-06-09 34 views
7

Tôi đang vật lộn để thực hiện một vỏ với đường ống cho lớp.Ống UNIX trên khối C đọc

typedef struct { 
    char** cmd; 
    int in[2]; 
    int out[2]; 
} cmdio; 

cmdio cmds[MAX_PIPE + 1]; 

Commands trong các đường ống được đọc và lưu trữ trong cmds.

cmdio[i].in là cặp mô tả tệp của đường dẫn đầu vào được trả về bởi pipe(). Đối với lệnh đầu tiên, mà đọc từ đầu vào đầu cuối, nó chỉ là {fileno (stdin), -1}. cmdin[i].out tương tự với đầu ra của ống/đầu ra. cmdio[i].in giống với cmd[i-1].out. Ví dụ:

$ ls -l | sort | wc 

CMD: ls -l 
IN: 0 -1 
OUT: 3 4 

CMD: sort 
IN: 3 4 
OUT: 5 6 

CMD: wc 
IN: 5 6 
OUT: -1 1 

Chúng tôi vượt qua mỗi lệnh để process_command, mà hiện một số điều:

for (cmdi = 0; cmds[cmdi].cmd != NULL; cmdi++) { 
    process_command(&cmds[cmdi]); 
} 

Bây giờ, bên trong process_command:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Vấn đề là đọc từ khối ống mãi mãi:

COMMAND $ ls | wc 
Created pipe, in: 5 out: 6 
Foreground pid: 9042, command: ls, Exited, info: 0 
[blocked running read() within wc] 

Nếu, thay vì trao đổi quá trình với execvp, tôi chỉ làm điều này:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout)); 
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 

    char buf[6]; 
    read(fileno(stdin), buf, 5); 
    buf[5] = '\0'; 

    printf("%s\n", buf); 
    exit(0); 
} 

Nó xảy ra để làm việc:

COMMAND $ cmd1 | cmd2 | cmd3 | cmd4 | cmd5 
Pipe creada, in: 11 out: 12 
Pipe creada, in: 13 out: 14 
Pipe creada, in: 15 out: 16 
Pipe creada, in: 17 out: 18 
hola! 
Foreground pid: 9251, command: cmd1, Exited, info: 0 
Foreground pid: 9252, command: cmd2, Exited, info: 0 
Foreground pid: 9253, command: cmd3, Exited, info: 0 
Foreground pid: 9254, command: cmd4, Exited, info: 0 
hola! 
Foreground pid: 9255, command: cmd5, Exited, info: 0 

gì có thể là vấn đề?

Trả lời

4

Bạn không có đủ đóng. Trong đoạn mã:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Sau khi bạn đã sao chép các đường ống để stdinstdout (thông qua fileno()), bạn cần phải đóng các đường ống:

if (!(pid_fork = fork())) { 
    dup2(cmd->in[0], fileno(stdin)); 
    dup2(cmd->out[1], fileno(stdout));  
    if (cmd->in[1] >= 0) { 
     if (close(cmd->in[1])) { 
      perror(NULL); 
     } 
    } 
    if (cmd->out[0] >= 0) { 
     if (close(cmd->out[0])) { 
      perror(NULL); 
     } 
    } 
    close(cmd->in[0]); // Or your error checked version, but I'd use a function 
    close(cmd->out[1]);  
    execvp(cmd->cmd[0], cmd->cmd); 
    exit(-1); 
} 

Các chương trình không kết thúc vì không một đầu ghi của tệp vẫn mở. Ngoài ra, đừng quên rằng nếu quá trình cha mẹ (vỏ) tạo ra các đường ống, nó phải đóng cả hai đầu của đường ống. Không đóng đủ ống có lẽ là sai lầm phổ biến nhất khi bắt đầu học đường ống dẫn nước với đường ống.

+0

Hum, không hoạt động: \ Vẫn như cũ, ngoại trừ stdin/stdout bị đóng trước khi chạy lệnh, ví dụ 'cat' không thành công với' cat: stdin: Bad descriptor '. Nhưng ngay cả khi tôi tránh đóng stdin/out hành vi cũng giống như trước đây. Theo tôi hiểu cả hai 'cmd -> (in | out) [(0 | 1)]' tham chiếu đến cùng các tệp cơ bản như 'fileno (std (in | out)', phải không? –

+0

Vâng , mỗi ống có một bộ mô tả đọc và một bộ mô tả ghi.Khi tôi hiểu nó, bạn có hai đường ống giữa cha mẹ và con (hoặc giữa hai đứa con).Trong mỗi tiến trình con, bạn sẽ chỉ muốn một 'dup2()' của đầu đọc của một đường ống mở trên 'stdin' và chỉ là' dup2() 'của đầu ghi của đường ống khác mở trên' stdout'; tất cả bốn đầu của đường ống đầu tiên phải được đóng lại (đây là điều khiến người ta ngạc nhiên). Tôi không chắc liệu chúng ta có đủ mã để nói điều gì khác đang xảy ra không. –

+0

Cảm ơn Jonathan, tôi đã không đóng cả hai đầu của đường ống từ cha mẹ như bạn đã nói. Cảm ơn rất nhiều! –

1

Được rồi, cuối cùng tôi đã giải quyết được.

Trên quá trình cha mẹ, ngay sau khi toàn bộ ngã ba đứa trẻ, tôi đặt:

f (cmd->in[0] != fileno(stdin)) { 
    close(cmd->in[0]); 
    close(cmd->in[1]); 
} 

và thì đấy. Tôi đã làm một cái gì đó như thế trước đây nhưng tôi nhầm lẫn và đã làm close(cmd->out[0]); thay thế. À chính nó đấy. Nó có ý nghĩa bây giờ.

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