상위 프로세스 블록은 좀비 하위 프로세스의 출력을 읽으려고 시도합니다.

상위 프로세스 블록은 좀비 하위 프로세스의 출력을 읽으려고 시도합니다.

설정:Python(3.6) 스크립트가 있습니다("운영자")는 bash 스크립트("라고 함)를 실행합니다.플런저")는 표준 하위 프로세스 방식으로 하위 프로세스에서 표준 출력을 수집하고 기록합니다.플런저스크립트 자체는 간단합니다. 다른 스크립트/프로그램을 호출하여 적당히 복잡한 세 가지 작업을 수행합니다. a) 여러 데몬을 종료하고, b) 일부 관리 작업을 수행하고, c) 몇 가지 새 데몬을 시작한 다음 종료합니다. 시스템 자체에는 특별히 이상한 점은 없습니다. 표준 rpm으로 실행되는 평범하고 오래된 CentOS입니다.

질문:언제. . . 언제플런저스크립트는 a와 b 부분만 실행하며 모든 것이 예상대로 작동합니다.플런저(c 없이) 완료될 때까지 실행되고,운영자모든 출력을 수집하고 나머지 작업을 계속합니다. 그러나 c 단계를 포함하면플런저올바르게 실행되고,운영자모든 출력이 수집되지만(한 번에 조금씩 읽으면) 플런저가 종료된 것을 전혀 눈치 채지 못하고 출력 읽기를 완료하지 않으므로 제어가 다시 전달되지 않습니다.운영자스크립트.

간단한 예:

return subprocess.check_output("plunger")  # doesn't complete with the real plunger script

관찰 결과:

  • 달리기플런저대화형 셸에서는 항상 잘 작동합니다.
  • 이것플런저프로세스가 해야 할 모든 작업을 수행하고 종료되었습니다.
  • ps를 실행하여 표시플런저bash 프로세스를 좀비로 사용("플런저")
  • Popen을 사용하고 한 줄씩 읽는 것은 예상되는 모든 줄이 출력되고 개행으로 올바르게 종료됨을 의미합니다.
  • Popen을 사용하고 poll()을 사용하여 프로세스 상태를 확인하면 None만 방출됩니다.
  • 자식 프로세스가 종료되지 않았거나 읽을 바이트가 아직 있는 것처럼 동작합니다. 비록 종료되었고 유일한 PIPE 스트림이 stdout이고 stdout 블록에서 읽는 경우에도 마찬가지입니다.

추측: 내 추측으로는 마지막 단계에서 생성된 새로운 백그라운드(데몬) 프로세스가 어떻게든 stdout 스트림을 상속하고 열어두기 때문에 실행된 플런저 스크립트가 출력되고 종료되더라도 일부 알 수 없는 프로세스가 이를 계속 유지하므로 운영자 스크립트는 계속할 수 없습니다.

질문: 내 추측이 가능합니까(또는 가능성이 있습니까)? 그렇지 않다면 무엇을 더 찾을 수 있습니까? 그렇다면 어떻게 보호해야 할까요?운영자그리고/또는플런저다운스트림에서 내 스트림을 남용하나요?

추신: 내 끔찍한 hacky fugly 솔루션은 다음과 같습니다.플런저작업을 마친 후 독특한 라인을 울려 퍼지게 할 때운영자보면 죽인다플런저프로세스. 이 글을 쓰는 것만으로도 기분이 더러워집니다.

편집 및 결론: 내 추측은 맞았습니다. 문제는 Python이나 실제로 bash와 관련이 없으며 포크 작동 방식과 더 관련이 있습니다. 다음은 최소한의 예입니다.

$ (date; (sleep 5 &); date); date
Wed Feb  6 12:46:27 EST 2019
Wed Feb  6 12:46:27 EST 2019
Wed Feb  6 12:46:27 EST 2019
$ (date; (sleep 5 &); date) | cat; date
Wed Feb  6 12:46:51 EST 2019
Wed Feb  6 12:46:51 EST 2019
Wed Feb  6 12:46:56 EST 2019  # <- five second gap!
$ (date; ((sleep 5 &)>/dev/null); date) | cat; date
Wed Feb  6 12:47:13 EST 2019
Wed Feb  6 12:47:13 EST 2019
Wed Feb  6 12:47:13 EST 2019
# this works too
$ (date; (sleep 5 >/dev/null &); date) | cat; date
Wed Feb  6 13:11:24 EST 2019
Wed Feb  6 13:11:24 EST 2019
Wed Feb  6 13:11:24 EST 2019

나는 이것으로부터 실제로 보호할 방법이 없다고 생각합니다. 실제 범인은 데몬을 시작하기 위해 C가 호출한 스크립트가 파이프를 열린 상태로 유지하지 않도록 출력을 다른 것으로 리디렉션해야 한다는 것입니다.

답변1

나는 질문의 마지막 부분(현재 편집됨)에서 이 질문에 답변했습니다.

간단히 말해서, 아무리 깊게 중첩되어 있어도 백그라운드 프로세스를 시작하는 모든 항목은 출력 스트림을 캡처하여 하위 프로세스를 완전히 종료하지 못하게 할 수 있습니다. 내가 생각해낸 해결책은: (a) 데몬 시작 출력을 /dev/null로 리디렉션하거나 (b) 데몬 시작 출력을 파일로 리디렉션하고 (원하는 경우) 해당 파일을 별도로 모니터링하는 것입니다. 직계 자녀가 종료됩니다.

관련 정보