# Python subprocess/psutil: 15년 묵은 Busy-loop 폴링을 끝내고 진정한 이벤트 기반 대기로 전환

> Clean Markdown view of GeekNews topic #26263. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=26263](https://news.hada.io/topic?id=26263)
- GeekNews Markdown: [https://news.hada.io/topic/26263.md](https://news.hada.io/topic/26263.md)
- Type: news
- Author: [darjeeling](https://news.hada.io/@darjeeling)
- Published: 2026-01-31T01:35:59+09:00
- Updated: 2026-01-31T01:35:59+09:00
- Original source: [gmpy.dev](https://gmpy.dev/blog/2026/event-driven-process-waiting)
- Points: 12
- Comments: 0

## Summary

Python의 **`subprocess`와 `psutil`** 모듈이 15년 만에 Busy-loop 폴링을 버리고 진정한 이벤트 기반 대기로 전환합니다. 이제 Linux에서는 `pidfd_open()`과 `poll()`, BSD·macOS에서는 `kqueue()`를 활용해 프로세스 종료를 감지하며, CPU는 불필요하게 깨어나지 않습니다. 이로써 대규모 프로세스 모니터링 환경에서도 컨텍스트 스위칭이 줄고, 대기 중 CPU 사용률이 사실상 0에 수렴하게 됩니다.

## Topic Body

요약:  
* Python의 `subprocess` 모듈과 `psutil` 라이브러리는 지난 15년 동안 프로세스 종료 대기(`wait()`) 시 `sleep`과 `waitpid`를 반복하는 비효율적인 'Busy-loop 폴링' 방식을 사용해왔음.  
* 이 방식은 불필요한 CPU Wake-up, 배터리 소모, 프로세스 종료 감지 지연(Latency) 문제를 야기하며, 다수의 프로세스를 모니터링할 때 확장성(Scalability)이 떨어짐.  
* 최근 업데이트를 통해 Linux에서는 `pidfd_open()`과 `poll()`, BSD/macOS에서는 `kqueue()`를 활용한 진정한 '이벤트 기반 대기(Event-driven waiting)'가 구현됨.  
* Windows는 이미 `WaitForSingleObject`를 사용하고 있어 변경 사항이 없으나, POSIX 시스템에서는 불필요한 컨텍스트 스위칭이 제거되고 CPU 사용량이 '0'에 수렴하게 됨.  
  
상세요약:  
**1. 15년 동안 지속된 문제: Busy-loop 폴링**  
Python 3.3에서 `subprocess.Popen.wait()`에 `timeout` 파라미터가 추가된 이래로, Python 표준 라이브러리와 널리 쓰이는 `psutil` 라이브러리는 프로세스 종료를 기다리기 위해 비효율적인 방식을 사용해왔습니다.   
  
기존 로직은 다음과 같이 단순하지만 비효율적이었습니다:  
1. `waitpid(WNOHANG)`으로 프로세스 상태 확인 (Non-blocking)  
2. 종료되지 않았으면 잠시 `sleep()` (Exponential backoff 적용)  
3. 1번으로 돌아가 반복  
  
```python  
# 기존 방식 (개념 코드)  
import time, os  
  
def wait_busy(pid, timeout):  
    delay = 0.0001  
    while True:  
        # 프로세스 종료 여부 확인 (polling)  
        if os.waitpid(pid, os.WNOHANG) == (pid, status):  
            return status  
        time.sleep(delay)  
        delay = min(delay * 2, 0.040) # 최대 40ms까지 대기 시간 증가  
  
```  
  
이 방식은 다음과 같은 3가지 치명적인 단점이 있습니다.  
  
* **CPU Wake-ups:** 아무리 대기 시간을 늘려도 시스템은 주기적으로 깨어나 상태를 확인해야 하므로 CPU 사이클을 낭비하고 전력을 소모합니다.  
* **Latency (지연):** 프로세스가 실제로 종료된 시점과 `sleep`에서 깨어나 이를 감지하는 시점 사이에 필연적인 시간 차이가 발생합니다.  
* **Scalability (확장성):** 수백, 수천 개의 프로세스를 동시에 모니터링해야 하는 서버 환경에서는 이러한 오버헤드가 급격히 증가합니다.  
  
**2. 해결책: POSIX 시스템을 위한 이벤트 기반 대기 (Event-driven Waiting)**  
모든 POSIX 시스템은 파일 디스크립터(File Descriptor)의 상태 변화를 감지하는 메커니즘(`select`, `poll`, `epoll`, `kqueue`)을 제공합니다. 최근 Python과 `psutil`은 이를 프로세스 PID 감지에 활용하는 방식으로 개선되었습니다.  
  
* **Linux:** 2019년 Linux 5.3 커널에 도입된 `pidfd_open()` 시스템 콜을 활용합니다. 이는 프로세스 PID를 가리키는 파일 디스크립터를 반환하며, 이를 `poll()`이나 `epoll()`에 등록하여 프로세스 종료 이벤트를 감시할 수 있습니다. (Python 3.9부터 `os` 모듈에 추가됨)  
* **BSD / macOS:** `kqueue()` 시스템 콜의 `EVFILT_PROC` 필터를 사용하여 프로세스 이벤트를 효율적으로 모니터링합니다.  
* **Windows:** 이미 `WaitForSingleObject` API를 통해 이벤트 기반 대기를 지원하고 있었으므로 변경 사항이 없습니다.  
  
**3. 성능 개선 및 결과**  
이 변화를 통해 `wait()` 호출 시 프로세스는 커널 입장에서 'Interruptible sleep' 상태가 됩니다. 즉, **CPU를 전혀 소모하지 않고** 커널 공간에서 조용히 대기하다가, 프로세스 종료 시그널이 발생하면 즉시 깨어납니다.  
  
`/usr/bin/time -v` 등을 통해 벤치마킹한 결과, 기존 방식 대비 불필요한 컨텍스트 스위칭(Context Switching)이 획기적으로 감소했으며, 프로세스 종료 감지 속도 또한 즉각적으로 개선되었습니다. 이 업데이트는 `psutil` 라이브러리와 CPython 코어에 반영되어, 향후 Python 개발자들은 별도의 코드 수정 없이 성능 향상 혜택을 누릴 수 있게 되었습니다.

## Comments



_No public comments on this page._
