Представьте, что родительская программа запустила дочерний элемент, и стандартный вывод родителя был присоединен к стандартному входу дочернего элемента, а стандартный вывод дочернего элемента был присоединен к стандартному входу родителя.
stdin <- stdout
parent child
stdout -> stdin
Если дочерний элемент (асинхронно) непрерывно считывал данные из своего стандартного ввода и записывал данные в свой стандартный вывод, но родитель просто записывал в стандартный ввод дочернего элемента и вообще не читал из стандартного вывода дочернего элемента:
stdin| << stdout
parent child
stdout ==>==> stdin
будет ли в конечном итоге блокировка? Разделяют ли стандартный ввод и стандартный вывод какой-либо буфер? В частности, через C ++ std::cin
(istream) и std::cout
(ostream), если это необходимо, чтобы ответить. Требует ли стандарт, чтобы они делали или не разделяли такую вещь, или он оставляет это на усмотрение реализации?
Что случилось бы?
Вы не можете «прикрепить» файловый дескриптор процесса к файловому дескриптору другого процесса. Что вы делаете (если ваша операционная система поддерживает это) — это назначаете два файловых дескриптора на концах «канала». Каналы нигде не указаны в стандарте C / C ++ (они определены в POSIX), и вы не найдете никакой стандартной библиотечной функции C / C ++, которая вообще на них ссылается.
Как реализовано в Unix (и Unix-подобных) системах, канал — это немного больше, чем буфер где-то в операционной системе. Пока буфер не заполнен, процесс может записывать данные на входной конец канала; данные просто добавляются в буфер. Пока буфер не пуст, процесс может считывать данные из выходного конца буфера; данные удаляются из буфера и передаются процессу чтения. Если процесс пытается записать в канал, чей буфер заполнен, или прочитать из канала, чей буфер пуст, процесс «блокируется»: то есть он помечается планировщиком ядра как неработоспособный и остается в этом состоянии до труба может обработать его запрос.
Сценарий, описанный в вопросе, должен включать две трубы. Один канал используется для того, чтобы позволить stdout родителя отправлять данные в дочерний stdin, а другой — для того, чтобы позволить stdout дочернего объекта отправлять данные в stdin родителя. Эти две трубы полностью независимы друг от друга.
Теперь, если родительский элемент прекращает чтение из своего стандартного ввода, но дочерний процесс продолжает запись в свой стандартный вывод, тогда в конечном итоге буферный канал заполнится. (На самом деле это не займет очень много времени. Буферы канала не очень велики, и они не растут.) В этот момент дочерний блок блокирует попытки записи в канал. Если ребенок не многопоточный, то когда он блокируется, то все. Он перестает работать, поэтому он больше не читает с его стандартного ввода. И если дочерний элемент прекращает чтение из своего стандартного ввода, тогда другой канал скоро заполнится, и родительский блок также заблокирует попытку записи в стандартный вывод.
Таким образом, нет необходимости в совместном использовании ресурсов для достижения тупика.
Это очень известная ошибка в процессах, которые порождают ребенка и пытаются передать данные ребенку во время чтения ответа ребенка. Если читатель не поспевает за полученными данными, вероятна тупиковая ситуация. Вы найдете много информации об этом, выполнив поиск, например, «тупиковая ситуация с буфером буфера». Вот несколько примеров ссылок, просто наугад:
Рэймонд Чен, на MSDN: http://blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx
Прямо здесь, в StackOverflow (со ссылкой на Python, но проблема идентична): Может кто-нибудь объяснить тупик буфера трубы?
Дэвид Глассер, с 2006 года: http://web.mit.edu/6.033/2006/wwwdocs/writing-samples/unix-DavidGlasser.html («Эти ограничения не просто теоретические — их можно увидеть на практике из-за того, что ни одна из основных форм межпроцессного взаимодействия, позже разработанная в Unix, не наложена поверх трубы».)