использование EOF для сигнализации по неназванным каналам - PullRequest
8 голосов
/ 25 марта 2011

У меня есть тестовая программа, которая использует безымянные каналы, созданные с помощью pipe (), для связи между родительскими и дочерними процессами, созданными с помощью fork () в системе Linux.

Обычно, когда процесс отправки закрывает запись fd канала, процесс получения возвращается из read () со значением 0, указывая EOF.

Однако, похоже, что если я заполняю канал довольно большим объемом данных (может быть, 100 Кбайт0 до того, как получатель начинает читать, получатель блокирует после чтения всех данных в канале - даже если отправитель закрыл его.

Я проверил, что процесс отправки закрыл канал с помощью lsof, и кажется довольно ясным, что получатель заблокирован.

Что приводит к вопросу: является ли закрытие одного конца трубы надежным способом сообщить получателю, что данных больше нет?

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

Разрешение

Я был почти уверен, что первоначальное предположение было верным, что закрытие канала вызывает EOF на стороне читателя, этот вопрос был всего лишь выстрелом в темноте - подумав, может быть, я заметил какое-то тонкое поведение канала Почти каждый пример, который вы когда-либо видели с трубами, - это игрушка, которая посылает несколько байтов и выходит. Часто вещи работают иначе, когда вы больше не выполняете атомарные операции.

В любом случае я пытался упростить свой код, чтобы устранить проблему, и мне удалось найти мою проблему. В псевдокоде я заканчивал тем, что делал что-то вроде этого:

create pipe1
if ( !fork() ) {
    close pipe1 write fd
   do some stuff reading pipe1 until EOF
}
create pipe2
if ( !fork() )  {
   close pipe2 write fd
   do some stuff reading pipe2 until EOF
}
close pipe1 read fd
close pipe2 read fd
write data to pipe1
get completion response from child 1
close pipe1 write fd
write data to pipe2
get completion response from child 2
close pipe2 write fd
wait for children to exit

Дочерний процесс чтения pipe1 зависал, но только тогда, когда объем данных в канале стал значительным. Это происходило, хотя я закрыл канал, который читал child1.

Взгляд на источник показывает проблему. Когда я разветвлял второй дочерний процесс, он взял свою собственную копию дескрипторов файла pipe1, которые были оставлены открытыми. Даже если в канал должен записываться только один процесс, его открытие во втором процессе не позволяет ему перейти в состояние EOF.

Проблема не проявлялась с небольшими наборами данных, потому что child2 быстро заканчивал свою работу и выходил. Но с большими наборами данных child2 не возвращался быстро, и я оказался в тупике.

1 Ответ

6 голосов
/ 25 марта 2011

read должен возвращать EOF, когда авторы закрыли конец записи.

Поскольку вы выполняете конвейер, а затем разветвление, оба процесса будут открывать запись fd.Возможно, в процессе чтения вы забыли закрыть часть записи канала.

Предостережение: Прошло много времени с тех пор, как я программировал на Unix.Так что это может быть неточно.

Вот код из: http://www.cs.uml.edu/~fredm/courses/91.308/files/pipes.html. Посмотрите на "закрыть неиспользуемые" комментарии ниже.

#include <stdio.h>

/* The index of the "read" end of the pipe */
#define READ 0

/* The index of the "write" end of the pipe */
#define WRITE 1

char *phrase = "Stuff this in your pipe and smoke it";

main () {
  int fd[2], bytesRead;

  char message [100]; /* Parent process message buffer */

  pipe ( fd ); /*Create an unnamed pipe*/

  if ( fork ( ) == 0 ) {
    /* Child Writer */
    close (fd[READ]); /* Close unused end*/
    write (fd[WRITE], phrase, strlen ( phrase) +1); /* include NULL*/
    close (fd[WRITE]); /* Close used end*/
    printf("Child:  Wrote '%s' to pipe!\n", phrase);

  } else {

    /* Parent Reader */
    close (fd[WRITE]); /* Close unused end*/ 
    bytesRead = read ( fd[READ], message, 100);
    printf ( "Parent: Read %d bytes from pipe: %s\n", bytesRead, message);
    close ( fd[READ]); /* Close used end */
  } 
}
...