Watch at Ease세상을 읽고, 일상의 답을 찾다.

기술 · 실용 가이드

Python subprocess 시간 초과 후 자식이 남나요? 프로세스 그룹과 파이프를 감사하세요

terminate, 그룹 신호, 재귀 정리를 선택하기 전에 직접 자식, 후손, 세션, PGID와 상속된 파이프 writer를 추적합니다.

Original diagram mapping a Python parent, direct child, descendants and inherited pipe writer
원본 일러스트: signal 전에 process, group, pipe의 ownership boundary를 그립니다. Watch at Ease

시간 초과는 대기 작업이 기한을 넘었다는 사실만 보여 줍니다. 작업이 한 PID인지, 모든 후손이 한 그룹에 남았는지, stdout과 stderr writer가 닫혔는지는 별도 문제입니다.

한 launch token으로 topology와 pipe 상태를 보존하고 실제 소유 범위에 맞는 가장 좁은 플랫폼 수단을 사용합니다. 제한된 유예 후에만 escalation하고 실제 launcher로 종료와 closure를 검증합니다.

정리 전에 시간 초과 증거를 고정합니다

launch token, 부모와 직접 자식 PID, PPID chain, command, start time, session, PGID, shell/capture 설정을 leader 종료 전에 기록합니다.

leader 생존, 남은 descendants, communicate의 open pipe 대기를 각각 기록합니다.

PID가 아니라 ownership unit을 선택합니다

단일 process면 direct-child contract로 충분하지만 shell, worker, build tool이면 POSIX group, recursive set, Windows job 등의 소유 단위를 정의합니다.

검증하지 않은 PID에서 group을 추측해 signal하지 말고 leader와 membership을 기록하며 무관한 service를 제외합니다.

직접 만든 POSIX group만 제어합니다

전체 tree를 소유하면 start_new_session=True를 사용합니다. timeout 후 verified group에 graceful signal, bounded wait, escalation, direct-child reap 순서로 처리합니다.

threaded app에서 preexec_fn을 무작정 사용하지 말고 문서화된 parameter를 사용하며 session escape도 다시 확인합니다.

상속된 pipe를 별도 shutdown gate로 둡니다

모든 writer가 닫힐 때까지 capture pipe는 끝나지 않습니다. shell, worker pool, helper가 child 종료 후에도 handle을 유지할 수 있습니다.

TimeoutExpired partial output을 보존하고 확인된 writer를 처리한 뒤 pipe를 drain합니다.

플랫폼 분기로 결과를 증명합니다

적합한 group이 없으면 psutil로 descendants를 일찍 snapshot하고 terminate, wait, kill과 identity check를 수행합니다.

실 launcher에서 timeout을 재현해 child/tree 제거, pipe closure, output policy, reap, unrelated sentinel 생존을 확인합니다.

Original decision diagram for leader reaping, descendant exit, pipe closure and collateral safety
원본 일러스트: 네 개의 timeout acceptance gate를 모두 통과해야 합니다. Watch at Ease