У меня есть тестовая программа, которая использует безымянные каналы, созданные с помощью pipe (), для связи между родительскими и дочерними процессами, созданными с помощью fork () в системе Linux.
Обычно, когда отправляющий процесс закрывает запись fd канала, принимающий процесс возвращается из read () со значением 0, указывающим EOF.
Однако кажется, что если я заполняю канал довольно большим объемом данных (может быть, 100 Кбайт 0 до того, как получатель начнет чтение, получатель блокируется после прочтения всех данных в канале, даже если отправитель закрыл его.
Я убедился, что отправляющий процесс закрыл канал с помощью lsof, и кажется довольно очевидным, что получатель заблокирован.
Отсюда возникает вопрос: является ли закрытие одного конца канала надежным способом сообщить получателю, что данных больше нет?
Если да, и нет условий, которые могут привести к чтению ( ) блокировка по пустому закрытому 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 не возвращался быстро, и я оказался в тупике.