2012-06-09 9 views
7

Mam problemy z zaimplementowaniem powłoki z rurociągami dla klasy.Rury UNIX na bloku C podczas odczytu

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

cmdio cmds[MAX_PIPE + 1]; 

Komendy w potoku są odczytywane i przechowywane w cmds.

cmdio[i].in to para deskryptorów plików rury wejściowej zwrócona przez pipe(). Dla pierwszego polecenia, które czyta z terminala, jest to po prostu {fileno (stdin), -1}. cmdin[i].out jest podobny do wyjścia wyjściowego rury/zacisku. cmdio[i].in to to samo, co cmd[i-1].out. Na przykład:

$ 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 

Mijamy każde polecenie do process_command, który robi wiele rzeczy:

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

Teraz wewnątrz 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); 
} 

Problem polega na tym, że czytanie z bloki rur na zawsze:

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

Jeżeli zamiast wymiany proces z execvp, po prostu to zrobić:

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

Zdarza się działać:

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 

Co może być problemem?

Odpowiedz

4

Nie masz wystarczająco dużo zamknięć. W kodzie:

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

Po powielane rur do stdin i stdout (poprzez fileno()), trzeba zamknąć Rury

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

Programy nie kończą, ponieważ nie ma koniec zapisu pliku wciąż otwarty. Ponadto nie zapominaj, że jeśli proces macierzysty (powłoka) tworzy rury, musi zamknąć oba końce rury. Niezapięcie wystarczającej liczby rur jest prawdopodobnie najczęstszym błędem, gdy zaczyna się uczyć hydrauliki z rurami.

+0

Hum, nie działa: \ Jest wciąż taki sam, z wyjątkiem wejścia stdin/stdout przed uruchomieniem polecenia, na przykład 'cat' kończy się niepowodzeniem z' cat: stdin: Bad descriptor'. Ale nawet jeśli uniknę zamykania stdin/out zachowanie jest takie samo jak wcześniej. O ile mi zrozumieć zarówno 'cmd -> (w | out)? [(0 | 1)]' odnoszą się do tych samych plików bazowych jak 'FILENO (STD (w | out)', prawda –

+0

Dobrze , każda rura ma deskryptor odczytu i deskryptor zapisu. Jak rozumiem, masz dwie fajki między rodzicem a dzieckiem (lub między dwójką dzieci).W każdym procesie potomnym będziesz potrzebował tylko 'dup2()' końca odczytu jednej rury otwartej na 'stdin' i tylko' dup2() 'końca zapisu drugiej rury otwartej na' stdout'; wszystkie cztery oryginalne końce rury powinny być zamknięte (co zaskakuje ludzi). Nie jestem pewien, czy mamy dość kodu, aby powiedzieć, co jeszcze idzie źle. –

+0

Dzięki Jonathan, nie zamykałem obu końców rur od rodzica, tak jak powiedziałeś. Wielkie dzięki! –

1

Dobra, w końcu to rozwiązałem.

dniu procesu macierzystego, tuż po całej dziecięcej widelcem, kładę:

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

i voila. Zrobiłem coś takiego wcześniej, ale zamiast tego źle wpisałem i zrobiłem close(cmd->out[0]);. Więc to jest to. Ma to sens teraz.

Powiązane problemy