Вопрос

Этот код ниже для выполнения 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 (ребенок в данном случае) будет ждать вечно на всякий случай, если на нем будет больше написано, что это будет сделано ... это не так, но читатель просто НЕ ЗНАЕТ! -)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top