Сомнение в трубах в Unix - PullRequest
       9

Сомнение в трубах в Unix

0 голосов
/ 15 сентября 2009

Этот код ниже для выполнения 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);
    }
}

Ответы [ 3 ]

4 голосов
/ 15 сентября 2009

pipe + fork создает 4 файловых дескриптора, два являются входными

Перед вилкой у вас есть одна труба с одним входом и одним выходом.

После разветвления у вас будет одна труба с двумя входами и двумя выходами.

Если у вас есть два входа для канала (в которые пишет процесс) и два выхода (из которых читает процесс), вам нужно закрыть другой вход, иначе у считывателя также будет вход канала, который никогда не закрывается.

В вашем случае родитель является читателем, и в дополнение к выходному концу канала у него есть открытый другой конец или входной конец канала, который теоретически может быть написано В результате канал никогда не отправляет eof, потому что, когда дочерний объект выходит, канал все еще открыт из-за неиспользованного fd родителя.

Так что родительский тупик, ожидая, что он сам напишет.

3 голосов
/ 15 сентября 2009

Обратите внимание, что '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:
0 голосов
/ 15 сентября 2009

Если дочерний элемент не закрывается p[1], то этот FD открыт в двух процессах - родительском и дочернем. Родитель в конце концов закрывает его, но ребенок никогда не закрывает, поэтому FD остается открытым. Поэтому любой читатель этого FD (ребенок в данном случае) будет ждать вечно на случай, если на нем будет больше написано, что это будет сделано ... это не так, но читатель просто НЕ ЗНАЕТ! *

...