Вопрос
Этот код ниже для выполнения ls -l | туалет В коде, если я комментирую close (p [1]) в parent, тогда программа просто зависает, ожидая некоторого ввода. Почему так? Дочерний объект записывает вывод ls в p1, а parent должен был взять этот вывод из p0.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
main ()
{
int i;
int p[2];
pid_t ret;
pipe (p);
ret = fork ();
if (ret == 0)
{
close (1);
dup (p[1]);
close (p[0]);
execlp ("ls", "ls", "-l", (char *) 0);
}
if (ret > 0)
{
close (0);
dup (p[0]);
//Doubt, Commenting the line below does not work WHy?
close (p[1]);
wait (NULL);
execlp ("wc", "wc", "-l", (char *) 0);
}
}
Решение
pipe
+ fork
создает 4 файловых дескриптора, два из которых являются входными данными
Перед форком у вас есть один канал с одним входом и одним выходом.
После разветвления у вас будет один канал с двумя входами и двумя выходами.
Если у вас есть два входа для канала (в которые пишет процесс) и два выхода (из которых читает процесс), вам необходимо закрыть другой вход, иначе у считывателя также будет вход канала, который никогда не закрывается. р>
В вашем случае родительский объект является читателем, и в дополнение к выходному концу канала он имеет открытый другой конец или входной конец канала, который теоретически мог бы выполнять этот материал. быть записанным. В результате канал никогда не отправляет eof, потому что, когда дочерний объект выходит, канал все еще открыт из-за неиспользованного fd родителя. Р>
Таким образом, родительский тупик, ожидая, что он сам напишет.
Другие советы
Обратите внимание, что dup (p [1])
означает, что у вас есть два файловых дескриптора, указывающих на один и тот же файл. Он не закрывает p [1]
; Вы должны сделать это явно. Аналогично с dup (p [0])
'. Обратите внимание, что файловый дескриптор, читающий из канала, возвращает нулевые байты (EOF) только тогда, когда для канала нет открытых дескрипторов файла записи; пока последний дескриптор записи не будет закрыт, процесс чтения будет зависать бесконечно. Если вы dup ()
пишете в конец записи, к концу записи есть два открытых файловых дескриптора, и оба должны быть закрыты до того, как процесс чтения получит EOF.
Вам также не требуется или не требуется вызов wait ()
в вашем коде. Если список ls
больше, чем может удержать канал, ваши процессы будут заблокированы, и ребенок ожидает завершения ls
, а ls
ожидает ребенок, чтобы продолжить чтение данных, которые он написал.
Когда лишний материал удаляется, рабочий код становится:
#include <unistd.h>
int main(void)
{
int p[2];
pid_t ret;
pipe(p);
ret = fork();
if (ret == 0)
{
close(1);
dup(p[1]);
close(p[0]);
close(p[1]);
execlp("ls", "ls", "-l", (char *) 0);
}
else if (ret > 0)
{
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execlp("wc", "wc", "-l", (char *) 0);
}
return(-1);
}
В Solaris 10 он компилируется без предупреждения с помощью:
Black JL: gcc -Wall -Werror -Wmissing-prototypes -Wstrict-prototypes -o x x.c
Black JL: ./x
77
Black JL:
Если дочерний элемент не закрывает p [1]
, тогда этот FD открыт в двух процессах - родительском и дочернем. Родитель в конце концов закрывает его, но ребенок никогда не закрывает, поэтому FD остается открытым. Поэтому любой читатель этого FD (ребенок в данном случае) будет ждать вечно на всякий случай, если на нем будет больше написано, что это будет сделано ... это не так, но читатель просто НЕ ЗНАЕТ! -) р>