Почему подпроцесс все еще может писать в stdout после его закрытия?

Я нашел этот кусок кода subprocess документация, где stdout одного процесса передается в другой процесс:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

и я смущен этим stdout.close() звонок. Конечно закрывать ручку stdout предотвращает процесс от производить любой выход?

Итак, я провел эксперимент, и, к моему удивлению, процесс не был затронут им вообще:

from subprocess import Popen, PIPE

p1 = Popen(['python', '-c', 'import time; time.sleep(5); print(1)'], stdout=PIPE)
p2 = Popen(['python', '-c', 'print(input()*3)'], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
print('stdout closed')
print('stdout:', p2.communicate()[0])

# output:
# stdout closed
# [5 second pause]
# stdout: b'111n'

что здесь происходит? Почему процесс может записать в закрытый трубка?

2 ответов


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

внутри Popen звонки, действия, которые дублируют дескриптор файла, тем самым увеличивая количество ссылок: операции, такие как dup/dup2 и fork.

если мы fork дочерний процесс, который получает файловый дескриптор от родителя, этот файловый дескриптор является дубликатом, который указывает на тот же объект.

если родитель закрывает свою исходную копию этого дескриптора, это не влияет на тот, который находится в дочернем. И наоборот. Только если и родитель, и ребенок закрывают этот дескриптор, базовый объект open file/device исчезает; и только если нет дополнительных дескрипторов, ссылающихся на него.

Это верно, даже если дескрипторы имеют одинаковое количество; каждый процесс имеет собственную таблицу номеров дескрипторов файлов. Файловый дескриптор 3 в дочернем файле отличается от 3 в Родительском.


каждый процесс имеет свой собственный набор файловых дескрипторов. Вы можете (и должны) закрыть копию родителя, как только вам это не нужно; фактический канал (конечная точка) выживает до последние