Linux Too many open files 해결: EMFILE·FD 누수·프로세스 한도 구분하기
EMFILE과 ENFILE을 구분하고 실제 프로세스 한도와 FD 추이를 확인한다. Python의 별도 프로세스에서 7개 검사로 고갈·close 후 회복·예외 정리를 재현한다.
Linux 서버에서 Too many open files가 발생하면 먼저 오류가 EMFILE인지 확인하고, 실패한 프로세스의 실제 FD 한도와 열린 FD의 변화를 함께 본다. 한도 증설은 누락된 close를 고치지 못한다. Python 3의 별도 테스트 프로세스에서 한도를 64로 낮춰 FD 고갈과 회복을 재현하고, 정상 동시 사용량과 누수를 구분하는 점검 순서를 정리한다.
프로세스의 유한한 연결 슬롯과 하나를 해제해 여유를 만드는 모습을 표현한 개념 일러스트다. 실제 FD 개수나 커널 구조를 표시하는 도식은 아니다.
작성·문서 확인: 2026-11-07. 검증 환경: Linux, CPython 3.12.14. 합성 테스트이며 사용자 서버나 실제 서비스의 한도를 변경하지 않았다. Windows용 예제가 아니다.
EMFILE과 ENFILE을 먼저 구분한다
Linux open(2)은 EMFILE을 프로세스별 FD 한도 도달, ENFILE을 시스템 전체의 열린 파일 한도 도달로 구분한다. 오류 문자열만 기록하기보다 errno도 함께 남긴다. 두 오류를 같은 증설 절차로 취급하지 않는다.
FD는 디스크 파일에만 쓰이지 않는다. /proc/PID/fd 문서는 소켓·파이프·epoll 같은 항목도 설명한다. 따라서 “설정 파일은 몇 개 안 여는데?”라는 관찰만으로 원인을 배제할 수 없다.
프로세스 한도인 RLIMIT_NOFILE은 정확히는 열 수 있는 최대 FD 번호보다 1 큰 값이다. 단순한 파일 경로 개수 제한이 아니다. getrlimit(2)
현재 셸 대신 실패한 프로세스를 본다
아래는 운영 환경에 맞게 PID를 바꿔 사용하는 읽기 전용 진단 예시다. 이번 검증에서 사용자 프로세스에 실행한 명령은 아니다. PID 1234는 자리표시자다.
limits의 Max open files에서 soft·hard 값을 확인하고, fd 항목이 일반 파일인지 socket·pipe·anon_inode인지 살핀다. 접근 권한이나 프로세스 종료 때문에 읽을 수 없다면 “FD가 없다”고 해석하지 않는다. 목록은 변하는 프로세스의 한 시점 관찰이며 완전한 원자적 스냅샷이 아니다.
셸의 ulimit -Sn은 그 셸의 soft 한도를 보여준다. 서비스 매니저나 컨테이너에서 이미 시작한 프로세스의 한도가 같다는 증거는 아니다. 자식은 부모 한도를 상속하고 exec 뒤에도 유지한다는 동작은 getrlimit(2)의 NOTES에 설명돼 있다. 적용 여부는 실제 대상 프로세스에서 확인한다.
재현: 테스트 자식만 한도를 낮춘다
아래를 fd_limit_demo.py로 저장하고 Linux에서 python3 fd_limit_demo.py로 실행한다. 표준 라이브러리만 사용한다. 별도 Python 자식에서 soft 한도만 64로 설정하고 hard 한도는 유지한다. 초기 soft 한도가 64 미만이면 실행을 중단한다. 시스템 전체 설정이나 다른 프로세스 한도를 바꾸지 않는다.
/dev/null을 읽기 전용으로 열어 FD를 보관한다. 파일의 내용을 쌓거나 네트워크 연결을 만드는 실험은 아니다. Python resource 문서는 getrlimit·setrlimit의 인터페이스를 설명한다.
실제 실행 결과다.
일곱 검사에서 확인한 것
| 검사 | 실제 결과 | 해석 |
| 1. 자식 한도 | soft=64, hard 유지 | 테스트 프로세스에서만 작은 한도를 사용했다 |
| 2. FD 보관 | 새 open이 EMFILE로 실패 | 파일이 존재해도 FD를 얻지 못할 수 있다 |
| 3. 하나 해제 | 새 open 성공, 같은 번호 재사용 | close가 FD 슬롯을 반환했다 |
| 4. 전체 해제 | 보관했던 FD의 fstat이 EBADF | 이 테스트에서 보관한 핸들을 닫았다 |
| 5. 순차 반복 | with로 256번 열고 닫기 성공 | 누적 open 횟수와 동시 보유량은 다르다 |
| 6. 예외 경로 | with 내부 예외 뒤에도 FD 닫힘 | 정상 반환 경로만 검사하면 부족하다 |
| 7. 부모 한도 | 실행 전후 동일 | 테스트 한도 변경이 부모에 전파되지 않았다 |
64는 새 파일을 정확히 64개 열 수 있다는 뜻이 아니다. 표준 입력·출력·오류 등 이미 사용 중인 FD가 있다. 이 예제는 고갈까지 새로 연 개수를 고정해서 주장하지 않는다. 또한 FD 번호는 재사용될 수 있으므로, 실제 프로그램에서 오래된 번호를 보관했다가 뒤늦게 닫는 방식으로 정리해서는 안 된다.
증설 전에 누수와 정상 동시 사용량을 나눈다
동일한 부하 구간을 반복하며 FD 종류와 개수의 변화를 비교한다. 요청이 끝난 뒤에도 특정 종류가 계속 쌓이면 소유권·종료 경로를 추적할 단서다. 연결 풀이나 의도적인 캐시도 FD를 유지하므로, 숫자가 높다는 이유만으로 누수라고 단정하지 않는다.
예제 5번처럼 작업마다 수명이 끝나는 자원은 범위 안에서 닫고, 예외·타임아웃·취소 경로도 확인한다. 풀에 맡기는 자원이라면 소유자가 풀인지 호출자인지 명확히 하고, 무제한 동시 작업이나 무제한 풀 확장을 피한다. 정상 동시 사용량이 한도에 가까운 경우에는 동시성·풀 크기·필요 한도를 함께 조정한 뒤 같은 부하로 다시 확인한다.
이번 검증은 ENFILE, 실제 소켓 고갈, 서비스 매니저·컨테이너 설정 변경, 장시간 부하를 시험하지 않았다. with 예제의 성공도 전체 애플리케이션에 누수가 없다는 증거는 아니다. 운영에서는 변경 후 실제 프로세스 한도와 부하 종료 뒤 FD 추이를 다시 확인해야 한다.
English version:
Linux Too Many Open Files: Diagnose EMFILE, FD Leaks, and Process Limits