참고자료:
고객사 지원하면서 성능 이슈 분석 문의를 받으면 잊을만 하면 다시 들어오곤 하는 문의 중 하나가 open file descriptor 설정 관련 문의이다. 인터넷 검색하면 수많은 관련 자료들이 많이 나오는데 용어의 의미에 대한 정의부터 시작해서 명확하게 정리된 문서들이 별로 없었다. 적어도 내 짧은 지식과 식견으로 평가하기엔... 간단하게 FAQ 형식으로 정리해 보았다.


Q1. 리눅스 오픈 파일 현황 확인과 오픈 파일 임계치 설정을 정확히 할 수 있는 방법은?

 A1. 확인은 fs.file-nr, 설정은 fs.file-max로 합니다.

 리눅스에서는 MS 윈도우의 파일 핸들과 같은 개념인 'file descriptor'라는 것을 제공합니다. 우리가 흔히 오픈 파일 임계치 확인/설정용으로 알고 있는 fs.file-nr, fs.file-max 등의 커널 파라미터 값은 정확히 표현하자면 '파일 디스크립터' 수를 확인/설정하기 위해 제공되는 것들입니다.
리눅스에서 오픈 파일은 그에 맞는 오픈 파일 디스크립터를 갖습니다. 따라서 파일 디스크립터(핸들러)에 대한 한계를 지정하게 되면 오픈 파일 갯수도 지정됩니다.

fs.file-nr에서 확인되는 '3번째' 필드값은 fs.file-max와 같습니다.
[root@localhost proc]# cat /proc/sys/fs/file-nr
3391     0     52427
|         |       |
|         |       |
|         |       최대 오픈 파일 디스크립터 수(fs.file-max)
|        할당되지 않은 파일 디스크립터 수 (2.6 커널에서는 항상 0으로 표기되며 이는 에러 아님)
할당된 파일 디스크립터 수



Q2 . lsof 로 확인되는 파일 숫자가 fs.file-nr 의 숫자보다 높게 확인되는 이유는?

 A2. 파일 디스크립터는 프로그램이 파일 핸들을 얻기 위해 사용하는 데이터 구조체입니다. 표준입력(키보드), 표준출력(모니터), 표준에러(모니터) 용도로 0, 1, 2가 대표적입니다. file-max 커널 파라미터는 오픈 파일 디스크립터를 다루며, file-nr은 현재의 오픈 파일 디스크립터 수를 제공합니다.
 그러나 lsof는 파일 디스크립터를 사용 중이지 않은 파일(현재 작업 중인 디렉토리, 메모리 맵핑된 라이브러리 파일, 실행 가능한 텍스트 파일 등이 이에 해당)까지 포함한 모든 오픈 파일을 출력합니다. 그래서 fs.file-nr 보다는 다소 많은 수가 출력됩니다.
 lsof는 모든 프로세스 내에 있는 파일 디스크립터 테이블 엔트리 정보를 출력합니다. 이런 경우에 커널 파일 핸들러(파일 디스크립터)와 값이 다를 수 있습니다. 왜냐하면 프로세스 fork() 등을 할 경우 한 번 오픈된 파일을 여러 개의 프로세스가 공유하면서 갯수가 증가할 수 있는 경우나 공유라이브러리를 많은 프로세스에서 사용하는 경우 lsof에서 같은 파일이 여러 번 중복 출력되기 때문입니다. 따라서 모니터링 시 file-nr을 이용하면 됩니다.

 어떤 파일을 vi로 열었을 때의 lsof를 예로 들어 보겠습니다.  lsof는 프로세스 내에 있는 파일 디스크립터 테이블 엔트리 정보를 출력합니다. 엔트리 테이블은 프로세스마다 별도로 갖습니다.
[root@localhost ~]# lsof | grep test.txt
vim        7250      root    3u      REG                8,3      12288    8126466 /root/.test.txt.swp
[root@localhost ~]# lsof -p 7250
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
vim     7250 root  cwd    DIR    8,3     4096 11010049 /root
vim     7250 root  rtd    DIR    8,3    24576        2 /
vim     7250 root  txt    REG    8,3  2882592 53262517 /usr/bin/vim
vim     7250 root  mem    REG    8,3  1262288 53380313 /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libperl.so
vim     7250 root  mem    REG    8,3    17888 32211387 /lib64/libattr.so.1.1.0
vim     7250 root  mem    REG    8,3    27920 32211388 /lib64/libacl.so.1.1.0
vim     7250 root  mem    REG    8,3    48600 32211391 /lib64/libcrypt-2.5.so
vim     7250 root  mem    REG    8,3    95464 32211390 /lib64/libselinux.so.1
vim     7250 root  mem    REG    8,3   247496 32211389 /lib64/libsepol.so.1
vim     7250 root  mem    REG    8,3   144776 32211289 /lib64/ld-2.5.so
vim     7250 root  mem    REG    8,3  1718232 32211290 /lib64/libc-2.5.so
vim     7250 root  mem    REG    8,3   614992 32211291 /lib64/libm-2.5.so
vim     7250 root  mem    REG    8,3    23360 32211297 /lib64/libdl-2.5.so
vim     7250 root  mem    REG    8,3   145872 32211293 /lib64/libpthread-2.5.so
vim     7250 root  mem    REG    8,3    25984 53251583 /usr/lib64/libgpm.so.1.19.0
vim     7250 root  mem    REG    8,3   114352 32211313 /lib64/libnsl-2.5.so
vim     7250 root  mem    REG    8,3    92816 32211304 /lib64/libresolv-2.5.so
vim     7250 root  mem    REG    8,3    18152 32211314 /lib64/libutil-2.5.so
vim     7250 root  mem    REG    8,3   380336 53271560 /usr/lib64/libncurses.so.5.5
vim     7250 root  mem    REG    8,3 56446960 53265654 /usr/lib/locale/locale-archive
vim     7250 root  mem    REG    8,3   114640 53413348 /usr/share/vim/vim70/lang/ko/LC_MESSAGES/vim.mo
vim     7250 root  mem    REG    8,3    25464 53346561 /usr/lib64/gconv/gconv-modules.cache
vim     7250 root  mem    REG    8,3    14280 53346348 /usr/lib64/gconv/EUC-KR.so
vim     7250 root  mem    REG    8,3    46960 53346545 /usr/lib64/gconv/libKSC.so
vim     7250 root  mem    REG    8,3    53880 32210970 /lib64/libnss_files-2.5.so
vim     7250 root    0u   CHR  136,1      0t0        3 /dev/pts/1
vim     7250 root    1u   CHR  136,1      0t0        3 /dev/pts/1
vim     7250 root    2u   CHR  136,1      0t0        3 /dev/pts/1
vim     7250 root    3u   REG    8,3    12288  8126466 /root/.test.txt.swp
[root@localhost ~]# ll /proc/7250/fd
합계 0
dr-x------ 2 root root  0  7월  7 17:02 ./
dr-xr-xr-x 6 root root  0  7월  7 17:02 ../
lrwx------ 1 root root 64  7월  7 17:02 0 -> /dev/pts/1
lrwx------ 1 root root 64  7월  7 17:02 1 -> /dev/pts/1
lrwx------ 1 root root 64  7월  7 17:02 2 -> /dev/pts/1
lrwx------ 1 root root 64  7월  7 17:02 3 -> /root/.test.txt.swp
[root@localhost ~]#

 PID가 7250 인 이 프로세스는 단 4개의 오픈 파일 디스크립터를 가지고 있지만(/proc/7250/fd), 29개의 연관된 오픈 파일이 존재합니다. 라이브러리 파일, 프로그램 자신(실행 텍스트) 등 파일 디스크립터를 사용하지 않는 오픈 파일들 중의 일부가 출력됩니다. 이러한 파일들은 커널 데이터 구조체에서 다른 용도로 집계되나(예를 들자면, 라이브러리를 확인하기 위해 /proc/PID/maps을 cat으로 열어보는 방법이 있습니다), 그들은 파일 디스크립터를 사용하지 않으므로 커널 파일 디스크립터 최대값을 소모시키지 않습니다.


Q3. . ulimit 설정도 '오픈 파일 수' 임계치가 아닌 '오픈 파일 디스크립터 수' 임계치를 다루는 명령어인지?

 A3. 그렇습니다.
 fs.file-nr이 system wide 설정이므로 이 한도 내에서 user별 unique 설정인 ulimit 설정을 해야 합니다. fs.nr_open은 프로세스당 오픈 파일 디스크립터 system wide 값이므로 ulimit의 nproc(max number of processes) 설정 시 참고해야 하는 값이 됩니다.



Q4. RHEL 버전별 fs.nr_open/fs.file-max 기본값은?

RHEL4

RHEL5

RHEL6

fs.nr_open

1048576

1048576

1048576

fs.file-max

203450

102249

98852


※ RHEL4에서 fs.nr_open 파일은 실제론 없습니다. 커널 소스에서 확인된 값입니다.
Creative Commons License