Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 446 – 새로 생성되는 파일 디스크립터를 상속 불가능하게 만들기

Author:
Victor Stinner <vstinner at python.org>
Status:
Final
Type:
Standards Track
Created:
05-Aug-2013
Python-Version:
3.4
Replaces:
433

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

자식 프로세스로 파일 디스크립터가 누출되면 여러 가지 성가신 문제가 발생하며, 이는 잘 알려진 주요 보안 취약점입니다. subprocess 모듈의 close_fds 매개변수를 True로 설정하여 사용하는 방법을 모든 경우에 쓸 수 있는 것은 아닙니다.

이 PEP는 이러한 문제의 위험을 줄이기 위해 Python이 생성하는 모든 파일 디스크립터를 기본적으로 상속 불가능하게 만들 것을 제안합니다. 또한 이 PEP는 상속 불가능한 파일 디스크립터를 생성하는 원자적 플래그를 지원하는 운영 체제에서 멀티스레드 애플리케이션의 경쟁 조건을 수정합니다.

이 변경으로 기존 코드가 동작하지 않게 될 가능성이 높다는 것을 알고 있지만, 인류의 이익을 위해 그래도 진행합니다. (자세한 내용은 아래의 “하위 호환성” 절을 참고하십시오.)

근거

파일 디스크립터의 상속

운영 체제마다 파일 디스크립터의 상속을 다르게 처리합니다. Windows는 기본적으로 상속 불가능한 핸들을 생성하는 반면, UNIX와 Windows의 POSIX API는 기본적으로 상속 가능한 파일 디스크립터를 생성합니다. Python은 단일 코드 베이스를 유지하고 파일 디스크립터에 같은 타입을 사용하기 위해 Windows 네이티브 API보다 POSIX API를 선호하며, 따라서 상속 가능한 파일 디스크립터를 생성합니다.

한 가지 예외가 있습니다. os.pipe()는 Windows에서는 상속 불가능한 파이프를 생성하는 반면, UNIX에서는 상속 가능한 파이프를 생성합니다. 그 이유는 구현상의 부산물입니다. os.pipe()는 Windows에서는 CreatePipe()(네이티브 API)를 호출하는 반면, UNIX에서는 pipe()(POSIX API)를 호출합니다. CreatePipe() 호출은 Windows 98의 POSIX API에 pipe()가 도입되기 전인 1994년에 Python에 추가되었습니다. 이슈 #4708은 Windows에서 os.pipe()가 상속 가능한 파이프를 생성하도록 변경할 것을 제안합니다.

Windows에서의 파일 디스크립터 상속

Windows에서 파일 객체의 네이티브 타입은 핸들(C 타입 HANDLE)입니다. 이 핸들에는 자식 프로세스에서 핸들을 상속할 수 있는지를 정의하는 HANDLE_FLAG_INHERIT 플래그가 있습니다. POSIX API를 위해 C 런타임(CRT)도 파일 디스크립터(C 타입 int)를 제공합니다. 파일 디스크립터의 핸들은 _get_osfhandle(fd) 함수를 사용하여 얻을 수 있습니다. _open_osfhandle(handle) 함수를 사용하여 핸들로부터 파일 디스크립터를 생성할 수 있습니다.

CreateProcess()를 사용하면, 핸들은 상속 가능 플래그(HANDLE_FLAG_INHERIT)가 설정되어 있고 동시에 CreateProcess()의 bInheritHandles 매개변수가 TRUE인 경우에만 상속됩니다. 표준 스트림(0, 1, 2)을 제외한 모든 파일 디스크립터는 bInheritHandles가 TRUE여도 자식 프로세스에서 닫힙니다. spawnv() 함수를 사용하면 상속 가능한 모든 핸들과 상속 가능한 모든 파일 디스크립터가 자식 프로세스에 상속됩니다. 이 함수는 파일 디스크립터 배열을 전달하기 위해 STARTUPINFO 구조체의 문서화되지 않은 필드인 cbReserved2와 lpReserved2를 사용합니다.

CreateProcess()를 사용하여 표준 스트림(stdin, stdout, stderr)을 교체하려면 STARTUPINFO 구조체의 dwFlags 필드에 STARTF_USESTDHANDLES 플래그를 설정해야 하고, CreateProcess()의 bInheritHandles 매개변수를 TRUE로 설정해야 합니다. 따라서 표준 스트림을 하나라도 교체하면 상속 가능한 모든 핸들이 자식 프로세스에 상속됩니다.

subprocess 프로세스의 close_fds 매개변수의 기본값은 stdin, stdout, stderr 매개변수가 None이면 True(bInheritHandles=FALSE)이고, 그렇지 않으면 False(bInheritHandles=TRUE)입니다.

함께 보기:

Windows에서 일부 핸들만 상속하기

Windows Vista부터 CreateProcess()는 STARTUPINFO 구조체의 확장인 STARTUPINFOEX 구조체를 지원합니다. 이 새 구조체를 사용하면 상속할 핸들 목록인 PROC_THREAD_ATTRIBUTE_HANDLE_LIST를 지정할 수 있습니다. 자세한 내용은 Programmatically controlling which handles are inherited by new processes in Win32 (Raymond Chen, 2011년 12월)를 읽으십시오.

Windows Vista 이전에는 핸들을 상속 가능하게 만든 뒤 bInheritHandles=TRUE로 CreateProcess()를 호출할 수 있습니다. 이 방법은 다른 모든 핸들이 상속 불가능한 경우에 동작합니다. 여기에는 경쟁 조건이 있습니다. 다른 스레드가 bInheritHandles=TRUE로 CreateProcess()를 호출하면 두 번째 프로세스에도 핸들이 상속됩니다.

Microsoft는 경쟁 조건을 피하기 위해 잠금을 사용할 것을 제안합니다. Q315939: PRB: Child Inherits Unintended Handles During CreateProcess Call (최종 검토: 2006년 11월)을 읽으십시오. Python issue #16500 “Add an atfork module”은 이러한 잠금을 추가할 것을 제안하며, 이 잠금을 사용하면 경쟁 조건 없이 핸들을 상속 불가능하게 만들 수 있습니다. 이러한 잠금은 Python 스레드 사이의 경쟁 조건만 막아 줄 뿐이며, C 스레드는 보호되지 않습니다.

또 다른 방법은 상속해야 하는 핸들을 복제하고 복제된 핸들의 값을 자식 프로세스에 전달하여, 자식 프로세스가 DUPLICATE_CLOSE_SOURCE와 함께 DuplicateHandle()을 사용해 복제된 핸들을 가져올 수 있게 하는 것입니다. 핸들이 (두 번) 복제되기 때문에 부모 프로세스와 자식 프로세스 사이에서 핸들 값이 달라집니다. 부모 프로세스와 자식 프로세스 중 하나 또는 둘 다 이 변경을 처리하도록 수정되어야 합니다. 자식 프로그램을 수정할 수 없다면, 최종 자식 프로그램을 실행하기 전에 중간 프로그램을 사용하여 부모 프로세스로부터 핸들을 가져올 수 있습니다. 중간 프로그램은 자식 프로세스의 핸들을 부모 프로세스에 전달해야 합니다. 예를 들어 중간 프로세스가 실패한 경우처럼 모든 핸들을 가져오지 못했다면, 부모는 복제된 핸들을 닫아야 할 수도 있습니다. 명령줄을 사용하여 핸들 값을 전달한다면, 핸들이 복제될 때 그 값이 바뀌므로 명령줄도 수정해야 합니다.

모든 Windows 버전에서 동작하는 완벽한 해결책이 없기 때문에 이 PEP는 이 문제에 대한 해결책을 포함하지 않습니다. 이 사항은 Windows에서 핸들 또는 파일 디스크립터 상속에 의존하는 사용 사례가 충분히 알려질 때까지 보류하며, 그때 가장 좋은 해결책을 고르고 그 구현을 신중하게 테스트할 수 있을 것입니다.

UNIX에서의 파일 디스크립터 상속

POSIX는 C 함수 execv()가 호출될 때 파일 디스크립터를 자동으로 닫기 위해 파일 디스크립터에 close-on-exec 플래그를 제공합니다. close-on-exec 플래그가 해제된 파일 디스크립터는 자식 프로세스에 상속되고, 플래그가 설정된 파일 디스크립터는 자식 프로세스에서 닫힙니다.

이 플래그는 fcntl()을 사용하여 두 번의 시스템 호출(하나는 현재 플래그를 얻고, 다른 하나는 새 플래그를 설정)로 설정할 수 있습니다.:

int flags, res;
flags = fcntl(fd, F_GETFD);
if (flags == -1) { /* handle the error */ }
flags |= FD_CLOEXEC;
/* or "flags &= ~FD_CLOEXEC;" to clear the flag */
res = fcntl(fd, F_SETFD, flags);
if (res == -1) { /* handle the error */ }

FreeBSD, Linux, Mac OS X, NetBSD, OpenBSD, QNX는 ioctl()을 사용하여 한 번의 시스템 호출로 플래그를 설정하는 것도 지원합니다.:

int res;
res = ioctl(fd, FIOCLEX, 0);
if (!res) { /* handle the error */ }

참고: close-on-exec 플래그는 fork()에 아무런 영향을 주지 않습니다. 모든 파일 디스크립터가 자식 프로세스에 상속됩니다. Python issue #16500 “Add an atfork module”은 fork 시점에 코드를 실행하는 새 atfork 모듈을 추가할 것을 제안하며, 이 모듈을 사용하면 파일 디스크립터를 자동으로 닫을 수 있습니다.

상속 가능한 파일 디스크립터의 문제점

자식 프로세스로 “누출된” 상속 가능한 파일 디스크립터는 큰 버그를 일으키지 않기 때문에 대부분 눈에 띄지 않습니다. 그렇다고 해서 이러한 버그를 고치지 않아도 된다는 뜻은 아닙니다.

상속된 파일 디스크립터와 관련된 두 가지 흔한 문제는 다음과 같습니다.

  • Windows에서는 디렉터리 안에 열린 모든 파일 핸들이 닫히기 전까지 그 디렉터리를 삭제할 수 없습니다. 파일에서도 같은 문제가 발생하지만, 파일이 FILE_SHARE_DELETE 플래그(open()의 O_TEMPORARY 모드)로 생성된 경우는 예외입니다.
  • 리스닝 소켓이 자식 프로세스로 누출되면, 부모 프로세스와 자식 프로세스가 종료되기 전까지 소켓 주소를 재사용할 수 없습니다. 예를 들어 웹 서버가 어떤 처리를 위해 새 프로그램을 실행하고 그 프로그램이 끝나기 전에 서버가 재시작하면, TCP 포트가 여전히 사용 중이므로 서버를 시작할 수 없습니다.

오픈 소스 프로젝트에서 발생한 문제의 예는 다음과 같습니다:

  • Mozilla (Firefox): 2002-05부터 미해결 상태입니다
  • dbus 라이브러리: 2008-05에 수정되었으며(dbus 커밋), 자식 프로세스에서 파일 디스크립터를 닫습니다
  • autofs: 2009-02에 수정되었으며, CLOEXEC 플래그를 설정합니다
  • qemu: 2009-12에 수정되었으며(qemu 커밋), CLOEXEC 플래그를 설정합니다
  • Tor: 2010-12에 수정되었으며, CLOEXEC 플래그를 설정합니다
  • OCaml: 2011-04부터 미해결 상태입니다. “PR#5256: Unix.open_process* 함수로 연 프로세스가 열려 있는 모든 파일 디스크립터(소켓 포함)를 상속합니다”
  • ØMQ: 2012-08부터 미해결 상태입니다
  • Squid: 2012-07부터 미해결 상태입니다

누수된 파일 디스크립터로 인한 SELinux 문제에 대해서는 Excuse me son, but your code is leaking !!! (Dan Walsh, 2012년 3월)도 참고하십시오.

보안 취약점

민감한 파일 핸들과 파일 디스크립터가 누수되면 보안 취약점으로 이어질 수 있습니다. 신뢰할 수 없는 자식 프로세스가 누수된 파일 디스크립터를 통해 비밀번호 같은 민감한 데이터를 읽거나 부모 프로세스를 장악할 수 있습니다. 리스닝 소켓이 누수되면 자식 프로세스가 새 연결을 수락하여 민감한 데이터를 읽을 수 있습니다.

취약점의 예는 다음과 같습니다:

CERT Secure Coding Standards의 FIO42-C. Ensure files are properly closed when they are no longer needed도 읽어 보십시오.

subprocess 모듈에서 수정된 문제

상속된 파일 디스크립터 때문에 subprocess 모듈에서 4가지 문제가 발생했습니다:

이 문제들은 Python 3.2에서 subprocess 모듈의 서로 다른 4가지 변경으로 수정되었습니다:

  • 이제 파이프는 상속되지 않습니다.
  • 이제 close_fds 매개변수의 기본값은 True입니다. 단, Windows에서는 한 가지 예외가 있어서 표준 스트림 중 하나 이상이 대체되면 기본값이 False입니다.
  • 새로운 pass_fds 매개변수가 추가되었습니다.
  • C로 구현된 _posixsubprocess 모듈이 새로 만들어졌습니다.

상속 불가능한 파일 디스크립터의 원자적 생성

다중 스레드 애플리케이션에서는 파일 디스크립터가 상속 불가능하게 설정되기 전, 새 프로그램이 생성되기 직전에 상속 가능한 파일 디스크립터가 만들어질 수 있습니다. 이 경우 파일 디스크립터가 자식 프로세스로 누출됩니다. 파일 디스크립터를 처음부터 상속 불가능하게 생성하면 이 경쟁 조건을 피할 수 있습니다.

FreeBSD, Linux, Mac OS X, Windows를 비롯한 많은 운영 체제는 파일 디스크립터 생성 시점에 상속 가능 플래그를 원자적으로 해제하여 상속 불가능한 파일 디스크립터를 생성하는 기능을 지원합니다.

상속 불가능한 소켓을 생성하기 위해 Windows 7 SP1과 Windows Server 2008 R2 SP1에서 WSASocket()용 새 WSA_FLAG_NO_HANDLE_INHERIT 플래그가 추가되었습니다. 이 플래그를 이전 버전의 Windows(예: Windows XP SP3)에서 사용하면 WSASocket()이 WSAEPROTOTYPE 오류로 실패합니다.

UNIX에서는 파일과 소켓을 위한 새 플래그가 추가되었습니다:

  • O_CLOEXEC: Linux(2.6.23), FreeBSD(8.3), Mac OS 10.8, OpenBSD 5.0, Solaris 11, QNX, BeOS, 다음 NetBSD 릴리스(6.1?)에서 사용할 수 있습니다. 이 플래그는 POSIX.1-2008의 일부입니다.
  • socket() 및 socketpair()용 SOCK_CLOEXEC 플래그는 Linux 2.6.27, OpenBSD 5.2, NetBSD 6.0에서 사용할 수 있습니다.
  • fcntl(): F_DUPFD_CLOEXEC 플래그는 Linux 2.6.24, OpenBSD 5.0, FreeBSD 9.1, NetBSD 6.0, Solaris 11에서 사용할 수 있습니다. 이 플래그는 POSIX.1-2008의 일부입니다.
  • fcntl(): F_DUP2FD_CLOEXEC 플래그는 FreeBSD 9.1과 Solaris 11에서 사용할 수 있습니다.
  • recvmsg(): MSG_CMSG_CLOEXEC은 Linux 2.6.23, NetBSD 6.0에서 사용할 수 있습니다.

2.6.23보다 오래된 Linux에서는 O_CLOEXEC 플래그가 그냥 무시됩니다. 따라서 파일 디스크립터가 상속 불가능한지 확인하려면 fcntl()을 호출해야 합니다. FD_CLOEXEC 플래그가 없으면 O_CLOEXEC이 지원되지 않는 것입니다. 2.6.27보다 오래된 Linux에서는 소켓 유형에 SOCK_CLOEXEC 플래그가 설정되어 있으면 socket() 또는 socketpair()가 errno를 EINVAL로 설정하며 실패합니다.

새 함수:

  • dup3(): Linux 2.6.27(및 glibc 2.9)에서 사용할 수 있습니다
  • pipe2(): Linux 2.6.27(및 glibc 2.9)에서 사용할 수 있습니다
  • accept4(): Linux 2.6.28(및 glibc 2.10)에서 사용할 수 있습니다

2.6.28보다 오래된 Linux에서는 accept4()가 errno를 ENOSYS로 설정하며 실패합니다.

요약:

운영 체제 원자적 파일 원자적 소켓
FreeBSD 8.3 (2012) X
Linux 2.6.23 (2007) 2.6.27 (2008)
Mac OS X 10.8 (2012) X
NetBSD 6.1 (?) 6.0 (2012)
OpenBSD 5.0 (2011) 5.2 (2012)
Solaris 11 (2011) X
Windows XP (2001) Seven SP1 (2011), 2008 R2 SP1 (2011)

범례:

  • “Atomic File”: open()을 사용하여 상속되지 않는 파일 디스크립터를 원자적으로 생성할 수 있는 운영 체제의 최초 버전입니다.
  • “Atomic Socket”: 상속되지 않는 소켓을 원자적으로 생성할 수 있는 운영 체제의 최초 버전입니다.
  • “X”: 아직 지원되지 않습니다.

함께 보기

Python 3.3의 현황

Python 3.3은 모든 플랫폼에서 상속 가능한 파일 디스크립터를 생성합니다. 단, Windows에서 상속되지 않는 파일 디스크립터를 생성하는 os.pipe()는 예외입니다.

Python 3.3에는 상속되지 않는 파일 디스크립터의 원자적 생성과 관련된 새 상수와 함수인 os.O_CLOEXEC, os.pipe2(), socket.SOCK_CLOEXEC가 추가되었습니다.

UNIX에서 subprocess 모듈은 기본적으로 표준 스트림(0, 1, 2)과 pass_fds 매개변수의 파일 디스크립터를 제외한 모든 파일 디스크립터를 자식 프로세스에서 닫습니다. close_fds 매개변수가 False로 설정되면 상속 가능한 모든 파일 디스크립터가 자식 프로세스에 상속됩니다.

Windows에서는 subprocess가 기본적으로 자식 프로세스의 모든 핸들과 파일 디스크립터를 닫습니다. 표준 스트림(stdin, stdout 또는 stderr) 중 하나 이상이 대체되면(예: 파이프로 리디렉션되는 경우), 상속 가능한 모든 핸들과 파일 디스크립터 0, 1, 2가 자식 프로세스에 상속됩니다.

os.execv*() 및 os.spawn*() 계열의 함수를 사용하면 상속 가능한 모든 핸들과 상속 가능한 모든 파일 디스크립터가 자식 프로세스에 상속됩니다.

UNIX에서 multiprocessing 모듈은 os.fork()를 사용하므로 모든 파일 디스크립터가 자식 프로세스에 상속됩니다.

Windows에서 multiprocessing 모듈을 사용하면 상속 가능한 모든 핸들과 파일 디스크립터 0, 1, 2가 자식 프로세스에 상속되며, 표준 스트림을 제외한 모든 파일 디스크립터는 닫힙니다.

요약:

모듈 UNIX의 FD Windows의 핸들 Windows의 FD
subprocess, 기본값 STD, pass_fds 없음 STD
subprocess, stdout 교체 STD, pass_fds 모두 STD
subprocess, close_fds=False 모두 모두 STD
multiprocessing 해당 없음 모두 STD
os.execv(), os.spawn() 모두 모두 모두

범례:

  • “모두”: 상속 가능한 모든 파일 디스크립터 또는 핸들이 자식 프로세스에 상속됩니다
  • “없음”: 자식 프로세스에서 모든 핸들이 닫힙니다
  • “STD”: 파일 디스크립터 0 (stdin), 1 (stdout), 2 (stderr)만 자식 프로세스에 상속됩니다
  • “pass_fds”: 서브프로세스의 pass_fds 매개변수로 지정한 파일 디스크립터가 상속됩니다.
  • “not applicable”: UNIX에서는 multiprocessing이 fork()를 사용하므로 이 경우는 이 PEP의 영향을 받지 않습니다.

열려 있는 모든 파일 디스크립터 닫기

UNIX에서 subprocess 모듈은 자식 프로세스에서 거의 모든 파일 디스크립터를 닫습니다. 이 작업에는 열려 있는 파일 디스크립터가 몇 개 되지 않더라도 MAXFD번의 시스템 호출이 필요합니다. 여기서 MAXFD는 파일 디스크립터의 최대 개수입니다. 이 최댓값은 os.sysconf("SC_OPEN_MAX")로 읽을 수 있습니다.

MAXFD가 크면 이 작업이 느려질 수 있습니다. 예를 들어 MAXFD=655,000인 FreeBSD 빌드봇에서는 이 작업에 300 ms가 걸렸습니다. issue #11284: slow close file descriptors를 참고하십시오.

Linux에서 Python 3.3은 /proc/<PID>/fd/에서 열려 있는 모든 파일 디스크립터의 목록을 가져오므로, 성능은 MAXFD가 아니라 열려 있는 파일 디스크립터의 개수에 따라 달라집니다.

함께 보기:

  • Python issue #1663329: SC_OPEN_MAX가 크면 subprocess의 close_fds 성능이 떨어집니다.
  • Squid Bug #837033: Squid는 열린 FD에 CLOEXEC를 설정해야 합니다. “Xen PV 게스트에서는 각 자식 프로세스에서 32k번 이상의 close() 호출에 오랜 시간([12-56]초)이 걸립니다.”

제안

상속되지 않는 파일 디스크립터

다음 함수들은 새로 만드는 파일 디스크립터가 기본적으로 상속되지 않도록 수정됩니다:

  • asyncore.dispatcher.create_socket()
  • io.FileIO
  • io.open()
  • open()
  • os.dup()
  • os.fdopen()
  • os.open()
  • os.openpty()
  • os.pipe()
  • select.devpoll()
  • select.epoll()
  • select.kqueue()
  • socket.socket()
  • socket.socket.accept()
  • socket.socket.dup()
  • socket.socket.fromfd()
  • socket.socketpair()

os.dup2()는 여전히 기본적으로 상속 가능한 파일 디스크립터를 생성합니다. 아래를 참조하십시오.

가능한 경우에는 원자적 플래그를 사용하여 파일 디스크립터를 상속 불가능하게 만듭니다. 원자적 플래그를 사용할 수 없을 때는 대체 방식이 필요하므로 원자성은 보장되지 않습니다.

새 함수와 메서드

모든 플랫폼에서 사용할 수 있는 새 함수:

  • os.get_inheritable(fd: int): 자식 프로세스가 파일 디스크립터를 상속할 수 있으면 True를, 그렇지 않으면 False를 반환합니다.
  • os.set_inheritable(fd: int, inheritable: bool): 지정한 파일 디스크립터의 상속 가능 플래그를 설정합니다.

Windows에서만 사용할 수 있는 새 함수:

  • os.get_handle_inheritable(handle: int): 자식 프로세스가 핸들을 상속할 수 있으면 True를, 그렇지 않으면 False를 반환합니다.
  • os.set_handle_inheritable(handle: int, inheritable: bool): 지정한 핸들의 상속 가능 플래그를 설정합니다.

새 메서드:

  • socket.socket.get_inheritable(): 자식 프로세스가 소켓을 상속할 수 있으면 True를, 그렇지 않으면 False를 반환합니다.
  • socket.socket.set_inheritable(inheritable: bool): 지정한 소켓의 상속 가능 플래그를 설정합니다.

기타 변경 사항

UNIX에서 subprocess는 pass_fds 매개변수로 전달된 파일 디스크립터를 상속 가능하게 만듭니다. 파일 디스크립터는 자식 프로세스에서 fork() 이후, execv() 이전에 상속 가능하게 만들어지므로, 부모 프로세스에서는 파일 디스크립터의 상속 가능 플래그가 변경되지 않습니다.

os.dup2()에 선택적 매개변수 inheritable이 새로 추가되었습니다: os.dup2(fd, fd2, inheritable=True). fd2는 기본적으로 상속 가능하게 생성되지만, inheritable이 False이면 상속 불가능하게 생성됩니다.

os.dup2()가 os.dup()와 다르게 동작하는 이유는 os.dup2()의 가장 일반적인 사용 사례가 표준 스트림, 즉 stdin (0), stdout (1), stderr (2)의 파일 디스크립터를 교체하는 것이기 때문입니다. 표준 스트림은 자식 프로세스에 상속될 것으로 기대됩니다.

하위 호환성

이 PEP는 파일 디스크립터 상속에 의존하는 애플리케이션을 망가뜨립니다. 개발자들은 파일 디스크립터 상속을 이식성 있게 처리하는 고수준 Python 모듈 subprocess를 재사용하는 것이 좋습니다.

pass_fds 매개변수와 함께 subprocess 모듈을 사용하거나, 표준 스트림을 리디렉션하기 위해 os.dup2()만 사용하는 애플리케이션은 영향을 받지 않을 것입니다.

이제 파일 디스크립터가 기본적으로 상속 불가능하게 만들어지므로, Python은 더 이상 POSIX를 따르지 않습니다. Python은 POSIX를 따르도록 설계된 것이 아니라, 이식성 있는 애플리케이션을 개발할 수 있도록 설계되었습니다.

관련 작업

프로그래밍 언어 Go, Perl, Ruby는 새로 생성된 파일 디스크립터를 기본적으로 상속 불가능하게 만듭니다: Go는 1.0(2009)부터, Perl은 1.0(1987)부터, Ruby는 2.0(2013)부터 그렇습니다.

Python으로 작성된 SCons 프로젝트는 Windows에서 파일을 상속 불가능하게 만들기 위해 내장 함수 file()과 open()을 재정의합니다. win32.py를 참고하십시오.

기각된 대안

새 open_noinherit() 함수 추가

2007년 6월, Henning von Bargen은 자식 프로세스에 상속된 파일 디스크립터 문제를 해결하기 위해 새 open_noinherit() 함수를 추가하자고 python-dev 메일링 리스트에서 제안했습니다. 당시 subprocess 모듈의 close_fds 매개변수 기본값은 False였습니다.

메일 스레드를 읽어 보십시오: [Python-Dev] 서브프로세스 문제와 보안 위험을 피하기 위한 새 함수 “open_noinherit” 제안.

PEP 433

PEP 433, “파일 디스크립터 상속을 더 쉽게 억제하기”는 여러 다른 대안을 제안한 이전 시도였지만, 합의에 이르지 못했습니다.

Python 이슈