2015-01-06 4 views
9

В Python 2.7 документации модуля подпроцесс, я нашел следующие фрагменты:Замена оболочки трубопровода

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] 

Источник: https://docs.python.org/2/library/subprocess.html#replacing-shell-pipeline

Я не понимаю эту линию: p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.

Здесь p1 .stdout закрывается. Как разрешить p1 получать SIGPIPE, если p2 выходит?

+0

Возможный дубликат [Объяснить пример из модуля подпроцессов python] (http://stackoverflow.com/questions/6046779/explain-example-from-python-subprocess-module) –

ответ

4

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

`dmesg | grep hda` 

Если grep процесс по какой-то причине заканчивается до того dmesg делается записи выходных данных, dmesg получит SIGPIPE и прекратить себя. Это было бы ожидаемым поведением для процессов UNIX/Linux (http://en.wikipedia.org/wiki/Unix_signal).

В противоположность этому, в реализации Python с использованием subprocess, если p2 завершил работу до p1 делается вывод генерации, то SIGPIPE не присылают, потому что на самом деле еще процесс, глядя на трубу - самого Python сценария (тот который создал p1 и p2). Что еще более важно, сценарий смотрит на трубу, но не потребляет ее содержимое - эффект заключается в том, что труба удерживается на неопределенный срок, а p1 застревает в подвешенном состоянии.

Явное закрытие p1.stdout отсоединяется сценарий Python из трубы и делает его таким образом, что ни один процесс, кроме p2 не смотрит на трубу - таким образом, если p2 это конец, прежде чем p1, p1 правильно получает сигнал, чтобы закончить себя без чего-либо искусственно удерживая трубку открытой.

Вот альтернативно сформулировано объяснение: http://www.enricozini.org/2009/debian/python-pipes/

+0

«труба, из которой не происходит активного процесса »и« все еще процесс, смотрящий на трубу », на самом деле не являются точными формулировками. –

0

От docs:

The p1.stdout.close() call after starting the p2 is important in order for p1 to receive a SIGPIPE if p2 exits before p1. 

Сигнал SIGPIPE посылается процессу, когда он пытается написать к трубе без процесса, подключенного к разъему другой конец. Когда p2 создается с использованием stdin=p1.stdout, есть два процесса, связанные с трубой p1.stdout: родительский процесс python и p2. Даже когда p2 закрывается преждевременно, родительский процесс все еще работает, поэтому сигнал SIGPIPE не отправляется. p1.stdout.close() закрывает p1.stdout в процессе родителя/вызывающего абонента, оставляя dmesg единственным процессом с открытым файловым дескриптором.

Другими словами, если нет p1.stdout.close() то:

  • p1.stdout остается открытым в родительском процессе. Если p2 выходит (то есть никто не читает p1.stdout), p1 не будет знать, что никто не читает p1.stdout и будет продолжать писать до p1.stdout до тех пор, пока не будет заполнен соответствующий буфер рабочей станции OS.
  • в случае, если p2 выходит преждевременно, p1.stdout все равно будет открыт в родительском процессе, поэтому SIGPIPE не будет сгенерирован.
2

надеюсь, более систематическое объяснение:

  • труба является экземпляром под управлением операционной системы. Он имеет один конец чтения и один конец записи.
  • Оба конца могут быть открыты несколькими процессами. Тем не менее, есть еще одна труба. То есть, несколько процессов могут совместно использовать один и тот же канал.
  • Процесс, который открыл один из концов, содержит соответствующий дескриптор файла. Этот процесс может снова активироваться close()! Если процесс завершается, операционная система закрывает для вас соответствующий дескриптор файла.
  • Все задействованные процессы могут close() их дескриптор файла, представляющий считанный конец трубы. Ничего плохого в этом, это совершенно прекрасная ситуация.
  • Теперь, если процесс записывает данные в конец записи в трубе, и конец чтения больше не открывается (никакой процесс не содержит дескриптор открытого файла для конца чтения), операционная система, совместимая с POSIX, отправляет сигнал SIGPIPE на письмо процесс для него до знаю, что читателя больше нет.

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

cat bigfile | head -n5 

действительно читает весь большой файл? Нет, это не так, потому что cat извлекает сигнал SIGPIPE, как только head выходов (после прочтения 5 строк от stdin). Важное значение для оценки: cat был разработан на самом деле ответить на SIGPIPE (это важное инженерное решение;)): он перестает читать файл и выходит. Другие программы предназначены для того, чтобы игнорировать SIGPIPE (для этого они сами справляются с этой ситуацией - это распространено в сетевых приложениях).

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

Однако, ваш пример на самом деле не очень хороший. grep hda будет читать всего ввода. dmesg - это процесс, который выходит первым.

Смежные вопросы