티스토리 뷰

상황

  subprocess.Popen과 subprocess.PIPE를 사용하던 중, 특정 프로세스가 생성된 후 정상적으로 동작하지 않고 좀비 프로세스가 되어 버리는 상황이 발생하였다.

 

예시 커맨드 및 코드

ls -alh | python test.py | python test2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import subprocess
import traceback
import sys
 
# ls -alh | python test.py | python test2.py
cmd1 = ['ls''-alh']
cmd2 = ['python''test.py']
cmd3 = ['python''test2.py']
 
try:
    ps = subprocess.Popen(cmd1, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    ps2 = subprocess.Popen(cmd2, stdin=ps.stdout, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    res = subprocess.check_output(cmd3, stdin=ps2.stdout, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    trace_data = ''.join(traceback.format_exception(exc_value))
    print(f'{trace_data}')
    res = b''
else:
    ps.wait()
    ps2.wait()
    
print(res.decode())
 
cs
  • 11 ~ 13 번째 줄: 커맨드 실행

 

원인 (https://bugs.python.org/issue21619)

  프로세스를 연결하는 PIPE 과정에서 IO 버퍼 크기가 작아서 발생하는 문제였다. 

  Popen 실행 시, bufsize라는 값을 따로 지정하지 않으면 기본 값으로 -1이 들어간다. -1은 공식 문서에 따르면 시스템 기본 값인 io.DEFAULT_BUFFER_SIZE를 사용한다를 의미한다.

  그리고 io.DEFAULT_BUFFER_SIZE의 크기는 8192 바이트이다. 공식 bugs.python에서 설명되어 있지만 Popen을 통해 전달되는 데이터가 많아지면 버퍼의 크기가 크지 않아 문제가 발생할 수 있다.

  따라서 프로세스에서 버퍼를 통해 파이프라인을 연결할 수 있게 충분한 bufsize를 입력하면 해결된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import subprocess
import traceback
import sys
 
# ls -alh | python test.py | python test2.py
cmd1 = ['ls''-alh']
cmd2 = ['python''test.py']
cmd3 = ['python''test2.py']
MY_BUFSIZE = 2**24
 
try:
    ps = subprocess.Popen(cmd1, bufsize=MY_BUFSIZE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    ps2 = subprocess.Popen(cmd2, bufsize=MY_BUFSIZE, stdin=ps.stdout, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    res = subprocess.check_output(cmd3, stdin=ps2.stdout, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError as e:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    trace_data = ''.join(traceback.format_exception(exc_value))
    print(f'{trace_data}')
    res = b''
else:
    ps.wait()
    ps2.wait()
    
print(res.decode())
 
cs

 

참고

 

subprocess — Subprocess management

Source code: Lib/subprocess.py The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace seve...

docs.python.org

'Programming > Python' 카테고리의 다른 글

[socket] select 인자로 class object 사용하는 법  (0) 2020.10.19
[unpack] little endian으로 문자열 unpack  (0) 2019.07.08
[Mac] import weakref 오류  (0) 2018.09.14
[Python] RAW Socket  (0) 2018.07.18
idapython 스크립트 모음  (0) 2018.06.26
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31