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?
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 –
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. –
Dzięki Jonathan, nie zamykałem obu końców rur od rodzica, tak jak powiedziałeś. Wielkie dzięki! –