IT_Server/UNIX · Solaris

[Solaris] 32-비트 stdio 의 256 파일 디스크립터 제한에 대한 해결책

JJun ™ 2007. 7. 20. 08:56
LONG
 
프로그래밍 해결책

이 섹션은 개발자가 손쉽게 수정할 수 있는 새로운 어플리케이션들을 위해 작성되었습니다.

두개의 프로그래밍 인터페이스가 256 파일 디스크립터 접근을 지원합니다. 주의할 점은 최대 파일 디스크립터의 갯수는 여전히 256이라는 것입니다.

 
 
 
향상된 표준 I/O Open 함수들: fopen(3C), fdopen(3C), popen(3C)

현존하고 있는 소스들을 수정하는 수고를 덜기 위해서 확장 FILE 기능의 장점을 이용합니다. stdio open 함수의 mode 문자열이 새로운 플래그 F 에 의해 확장 됩니다. 예를 들어:

FILE *fptr = fopen("dummy.txt", "rF");
int fd = creat("dummy2.txt", S_IWUSR);
FILE *stream = fdopen(fd, "wF");
FILE *ptr = popen("/usr/bin/ls *.txt", "rF");

만약 mode 문자열의 마지막 문자가 F 라면 32-비트 프로세스들은 스트림을 255 이상의 파일 디스크립터와 연관시키는 것이 허용 됩니다. 64-비트 어플리케이션에서 어플리케이션은 F 문자를 조용히 무시해 버립니다. 이러한 마이너한 변경을 제외하고 기존에 존재하는 stdio open 함수의 구문에는 전혀 변화가 없습니다.

stdio open 함수의 F mode 문자열은 아래의 작업을 실행하지 않는 코드들을 위해서만 사용됩니다:

  • FILE 구조체의 멤버필드에 직접 접근하지 않음
  • FILE 포인터를 호출자에게 돌려줌

만약 어플리케이션이 이전에 언급되었던 패턴을 보여 준다면 문자열 F 는 확장 FILE 기능을 사용하기 위해 mode 문자열에 추가되어서는 안됩니다. 데이타 오류가 발생할 수 있기 때문입니다.

데이타 오류는 확장 FILE 포인터가 유저가 알 수 없는 바이너리 코드를 가르키도록 할 수 있습니다 -- 그럼으로서 호출자는 이것을 어떻게 처리해야 할지 모르게 됩니다. 이 인터페이스는 확장 FILE 포인터의 잘못된 사용을 막아 줄수 있는 안정장치를 제공하지 않습니다. 만약 FILE 포인터가 특정 코드에 반드시 리턴되어야 한다면 enable_extended_FILE_stdio(3C) 을 사용 할 것을 권장합니다.

위의 예제에서 아래의 라인을 수정한 후에 재빌드 합니다

fds[i] = fopen(filename, "w");

이렇게:

fds[i] = fopen(filename, "wF");

이제 파일 디스크립터의 제한을 증가시킨후에 테스트 케이스를 실행시키면 다음과 같은 결과를 얻을 수 있습니다:

% cc -o fopentestcaseF fopentestcase.c
% ulimit -n 10000
% ulimit -a | grep descriptors
nofiles(descriptors) 10000
% ./fopentestcaseF
Number of open files = 9996. fopen() failed with error:  
Too many open files

작동을 위한 어떠한 특수 라이브러리들도 지정되지 않은채 링크 되었음을 주의하시기 바랍니다. 모든 stdio 루틴들은 여전히 libc 의 일부입니다.

fopen(3C), fdopen(3C), popen(3C) 의 멘페이지를 참고하시기 바랍니다.

 
 
 
새로운 프로그래밍 인터페이스: enable_extended_FILE_stdio(3C)

만약 FILE 포인터가 계속적으로 하나의 함수 문맥에서만 사용되도록 제한되지 않는 다면 새로운 프로그래밍 인터페이스 enable_extended_FILE_stdio(3C) 를 이용해서 확장 FILE 설비를 활성화 할 수 있습니다. 이 인터페이스는 몇몇 보호 메카니즘을 제공함으로써 데이타의 오류를 최소화 시켜 줍니다. 예를 들어 인터페이스를 사용함으로써 사용자는 어플리케이션이 런타임시에 FILE -> _file 을 부적절하게 접근했을때 프로세스에게 전달될 시그널을 선택할 수 있습니다.

/usr/include/stdio_ext.h 헤더에 정의된 새로운 인터페이스는 다음과 같습니다:

int enable_extended_FILE_stdio(int, int);

첫번째 정수 매개변수는 비할당 파일 디스크립터로 선택할 3 에서 355 사이? 파일 디스크립터를 가르킵니다. -1 을 설정하게 되면 enable_extended_FILE_stdio(3C) 가 적절한 비할당 파일 디스크립트를 선택 할 수 있도록 합니다. 이것은 환경변수 _STDIO_BADFD 을 설정했을때와 비슷합니다.

두번째 정수 매개변수는 비할당 파일 디스크립터가 close(2) 혹은 closefrom(3C) 이외의 시스템 콜에 의해서 사용되었을때 발생할 시그널을 가르킵니다. 몇몇 어플리케이션들은 열지도 않은 파일 디스크립터를 닫으려고 시도할 것입니다. 이 예외는 어플리케이션이 비정상적으로 크래쉬 되는 것을 방지해 줍니다.

만약 -1 이 전달되면 기본 시그 SIGABRT 가 프로세스에 전달 됩니다. 값 0 은 시그널을 보내는 것을 비활성화 함으로써 FILE -> _file 참고 작업을 무시해 버립니다. 이외의 경우에는 지정된 시그널이 프로세스로 전달 됩니다 signal.h(3HEAD) 멘 페이지를 통해서 솔라리스의 전체 시그널 리스트를 참고하시기 바랍니다. 이것은 환경 변수 _STDIO_BADFD_SIGNAL 을 설정하는 것과 유사합니다.

enable_extended_FILE_stdio(3C) 함수는 오직 32-비트 컴파일 환경에서만 사용 가능합니다.

확장 FILE 설비가 유효하면 프로세스의 파일 디스크립터 제한을 256 이상으로 증가시켜도 언제든지 프로세스가 파일을 열 수 있습니다. rlim_fd_max 커널 파라미터의 정보를 확인하시기 바랍니다. 여러분은 ulimit/limit 커맨드를 이용하거나 프로그래밍 적으로/usr/include/sys/resource.h 에서 정의된 getrlimit(2)/setrlimit(2) 함수를 사용할 수 있습니다. functions defined in the /usr/include/sys/resource.h header.

아래에는 다음의 3가지를 보여주는 간단한 프로그래밍 예제가 있습니다:

  • 파일 디스크립터 제한을 getrlimit(2)/setrlimit(2) 인터페이스를 통해 설정
  • 확장 FILE 설비 를 활성화 하기 위한 새로운 함수의 사용 예
  • FILE -> _file 의 값을 직접 변경함으로써 어플리케이션의 크래쉬를 유도

아래의 코드를 컴파일하고 런타임 해결책 섹션에서 만들었었던 libthirdparty.so 라이브러리를 이용해 링크 합니다.

% cat enableextfilestdio.c
#include 
#include 
#include 
#include 
#define NoOfFiles 500
void manipulatefd(FILE *);
int main ()
{
        FILE *fptr;
        struct rlimit rlp;
        int i;
        (void) getrlimit (RLIMIT_NOFILE, &rlp);
	/* set the desired number of file descriptors */
        rlp.rlim_cur = NoOfFiles;
        if (setrlimit (RLIMIT_NOFILE, &rlp) == -1)
        {
                perror ("setrlimit(): ");
                exit (1);
        }
        if (enable_extended_FILE_stdio (-1, -1) == -1)
        {
                perror ("enable_extended_FILE_stdio(3C): ");
                exit (1);
        }
        for (i = 0; i < NoOfFiles; i++)
        {
                fptr = fopen ("/tmp/enable_test.txt", "w");
                if (fptr == NULL)
                {
			perror("\nfopen failed. ");
                        exit (1);
                }
                printf ("\nfd = %d", fileno(fptr));
                if (fileno (fptr) % 400 == 0)
                {
                        manipulatefd (fptr);
                }
        }
        return (0);
}
% export LD_LIBRARY_PATH=/tmp:$LD_LIBRARY_PATH
% cc -o enableextfilestdio -lthirdparty enableextfilestdio.c
% ./enableextfilestdio
fd = 3
fd = 4
fd = 5
...
...
fd = 398
fd = 399
fd = 400
thirdpartysrc.c : manipulatefd(): 
	underlying file descriptor = 196
Application violated extended FILE safety mechanism.
Please read the man page for extendedFILE.
Aborting
Abort (core dumped)

enable_extended_FILE_stdio(3C) 의 멘페이지에서 더 자세한 정보를 확인하시기 바랍니다.

경고: 솔라리스의 다음 메이저 고객 버전에서 _file_magic 으로 변경됨

확장 FILE 메카니즘을 사용하는데에 안정성을 보장하기 위해 FILE 구조체의 _file 멤버 필드는 완전히 _magic 으로 솔라리스 익스프레스, 빌드 39 이후의 오픈솔라리스 소스 코드에서 완전히 바뀌게 됩니다. 이러한 변경은 FILE -> _file 를 참조로 하는 모든 소스 코드의 컴파일에 영향을 미치게 됩니다. 주의: 이 경고는 업데이트를 포함한 솔라리스10 에는 적용되지 않습니다.

다음의 diff 출력이 FILE 구조체에서 확장 FILE 설비를 수용하도록 정의가 변경된 것을 보여 줍니다:

- unsigned char _file;  /* UNIX system file descriptor */
+ unsigned char _magic; /*Old home of the file descriptor*/
+          /* only fileno(3C) can retrieve the value now */
- unsigned  __filler:4;
+ unsigned  __extendedfd:1; /* enable extended FILE */
+ unsigned  __xf_nocheck:1; /*no extended FILE runtime check*/
+ unsigned  __filler:10;

그러므로 FILE -> _file 참조를 사용하는 프로그램은 솔라리스 익스프레스 혹은 빌드 39 이후의 오픈솔라리스 에서 아래의 오류와 함께 실패할 것입니다.

"filename.c", line xx: undefined struct/union member: _file
cc: acomp failed for filename.c

_file 는 더이상 FILE 의 파일 디스크립터 내에 존재하지 않습니다. 만약 코드가 단순히 _file 을 읽어 오는 것이라면 모든 소스코드를 적절한 fileno(FILE) 를 사용하는 것으로 교체해 주면 됩니다. fileno(3C) 의 멘페이지를 참고하시기 바랍니다. 이제 더이상 _file 에 새로운 값을 대입해 주면 안됩니다.

 
 
 
런타임 퍼포먼스의 영향

확장 FILE 설비가 활성화 되도 255 보다 작거나 같은 파일디스크립터를 접근하는데에는 어떠한 퍼포먼스 영향도 없습니다. 그러나 256 이상에서는 약간의 퍼포먼스 저하가 생길 수 있습니다. 파일 디스크립터를 임시 장소에 저장하고 읽어와야 하기 때문입니다.

 
 
 
패치와 버그

만약 여러분의 시스템이 솔라리스10 3/05 버전에서 11/06 버전 사이를 운용하고 있다면 확장 FILE 설비를 다음의 패치들을 설치 함으로써 사용 할 수 있습니다.:

SPARC 플랫폼:

x86/x64 플랫폼:

만약 어플리케이션 코드가 썬 스튜디오 소프트웨어 컴파일러에 기본적으로 제공되는 STLport 에 의해 링크되었다면 -library=stlport4 컴파일러 옵션을 사용하고 썬 스튜디오 11 소프트웨어를 SPARC 에서 121017-07 혹은 그 이후 그리고 x86 플랫폼에서 121018-07 이후 버전의 패치를 설치 해서 확장 FILE설비를 사용할 수 있도록 합니다.

확장 FILE 설비의 버그는 오픈솔라리스 버그 페이지 에 신고해주시거나 오픈솔라리스 토론 포럼 에 신고해주시기 바랍니다.

 
 
 
참고자료

PSARC/2006/162: Extended FILE Space for 32-Bit Solaris Processes

메뉴얼 페이지:

 
저자에 관하여

Giri Mandalika 은 썬 마이크로시스템즈 ISV-엔지니어링 그룹의 소프트웨어 엔지니어로 썬이 엔터프라이즈 어플리케이션을 실행하는데에 있어서 선호되는 기업이 되도록 노력하고 있습니다. Giri 는 텍사스 대학에서 컴퓨터 공학 박사 학위를 가지고 있습니다.


이 아티클의 원본보기 : 
http://developers.sun.com/solaris/articles/stdio_256.html

ARTICLE

역사적으로 32-비트 어플리케이션은 솔라리스 운영체제에서 C 라이브러리의 표준 I/O 함수들을 사용할때 파일 디스크립터를 오직 0 에서 255 까지만을 사용할 수 있도록 제한되었었습니다. 솔라리스10의 확장된 FILE 은 잘-동작하는 32-비트 어플리케이션이 표준 I/O 함수들을 이용해서 어떠한 유효한 파일 디스크립터도 사용할 수 있도록 합니다.

여기서 잘-동작하는 어플리케이션이란 다음의 3가지 요구조건을 만족하는 것을 의미합니다:

  • 표준 I/O 스트림과 연관되어 있는 FILE 포인터에 의해 가르켜지고 있는 FILE 구조체의 어떠한 멤버 필드에도 직접적으로 접근하지 않는다
  • 에러 상황에 대비해 표준 I/O 함수의 어떠한 리턴값들도 체크한다
  • 에러 상황이 보고되었을때 적절한 동작을 한다

이 글은 확장된 FILE 설비 하에서 소개된 런타임 및 프로그래밍 해결책에 대해 자세히 설명 합니다. 아래의 내용은 전부 32-비트 어플리케이션에 관계된 이야기 이므로 64-비트 어플리케이션들은 256 파일 디스크립터 제한에 전혀 상관이 없습니다.

 
 
역사적 배경
 

키워드 Solaris stdio open file descriptors 로 간단하게 찾아 본 결과 솔라리스 stdio 의 256 파일 디스크립터 제한에 대한 많은 자료들이 있었습니다. 1992 request for enhancement (RFE) 가 문제를 설명하고 있습니다: "32-비트 stdio 루틴들은 반드시 255 이상의 파일 디스크립터를 지원해야 한다." 버그 리포트 링크가 몇가지 다른 stdio 의 256 파일 디스크립터 제한에 대한 다른 버그들에 대한 링크를 포함하고 있습니다.

솔라리스의 이러한 제한은 표준 I/O 스트림과 연관된 파일 디스크립터의 값 이 unsigned char 형태로 저장되기 때문입니다. 어떠한 솔라리스 시스템에서도 찾을 수 있는 헤더 /usr/include/stdio_impl.h 에서 FILE 구조체의 정의를 보면 이 글에서 다루고 있는 해결책 을 다루고 있지 않음을 알 수 있습니다:

struct __FILE_TAG       
{
#ifdef _STDIO_REVERSE
        unsigned char   *_ptr;
        int             _cnt;   
#else
        int             _cnt;  
        unsigned char   *_ptr;  
#endif
        unsigned char   *_base;
        unsigned char   _flag;
        unsigned char   _file;  
			/* UNIX System file descriptor */
        unsigned        __orientation:2; 
        unsigned        __ionolock:1;   
        unsigned        __seekable:1;
        unsigned        __filler:4;
};

이름 __FILE_TAG 는 단지 FILE 의 alias 입니다. /usr/include/stdio_tag.h 을 보시기 바랍니다.

멤버 필드 _file 는 파일 디스크립터를 저장하고 있고 unsigned char 타입으로 정의되어 있습니다. unsigned char 는 메모리에서 8비트의 공간을 차지 합니다. 그러므로 _file 은 최고 2^8 즉 256 의 값을 가질 수 있습니다. 다시 말해서 _file 은 32-비트 프로세스당 256개의 파일 디스크립터 만을 가질 수 있도록 제한하고 있습니다. 이러한 제한은 명확하게 stdio(3C) 에 명시되어 있습니다.

썬은 더 많은 파일 디스크립터를 수용하도록 하려고 8-비트 unsigned char 를 16-비트 int 로 변경하지는 않았습니다. 왜냐하면 FILE 구조체의 사이즈를 변경 시키는 것이 솔라리스 이전 버전과의 바이너리 호환성을 망가뜨리는 일이기 때문입니다.

 
 
 
해결책

2007년 7월 버전의 솔라리스10 부터 썬은 stdio 의 256 파일 디스크립터 제한을 완화 시킬 수 있는 확장 FILE 설비의 형태로 런타임 그리고 프로그래밍적인 해결책을 제공합니다. 솔라리스 익스프레스 배포판 혹은 빌드 39 이후의 오픈솔라리스 배포판 은 이 해결책을 가질 것입니다. 이 글의 패치와 버그 섹션에서는 확장 FILE 설비를 솔라리스10 3/05 에서 11/06 까지의 배포판에 설치하는 법을 포함하고 있습니다.

 
 
 
런타임 해결책

섹션의 제목에서 제안하는 대로 런타임 해결책은 어떠한 종류의 소스코드 변경이나 오브젝트의 재컴파일도 필요로하지 않습니다. 그러나 확장 FILE 설비를 명시적으로 활성화 시키더라도 기존에 존재하는 32-비트 어플리케이션의 기본 동작에는 변함이 없을 것입니다. 이 기능을 활성화 시킨 어플리케이션은 어떠한 유효한 파일 디스크립터라도 표준 I/O 혹은 stdio -- 스트림 과 연관시킬 수 있을 것입니다. 파일 디스크립터는 0, 1 그리고 2 가 기본적으로 stdin, stdout, stderr I/O 스트림들을 위해 예약되어 있습니다.

여러분은 프로세스-당 최대 파일 디스크립터 숫자를 쉘에서 아래의 커맨드가 리턴하는 값의 이하 혹은 동일한 값으로 설정할 수 있습니다.

echo 'rlim_fd_max/D' | mdb -k | awk '{ print $2 }'

쉘에서 파일-디스크립터를 조정하기 위해서 sh/ksh/bash 에서는 ulimit -n 그리고 csh 에서는 limit descriptors 를 실행 합니다. 여기서 max_file_descriptors 는 여러분이 원하는 최대 파일 디스크립터 갯수 입니다.

프로세스가 한번에 열 수 있는 기본적인 파일 갯수의 제한은 65,536 개입니다. 여러분은 이 제한을 rlim_fd_max 를 통해 조정할 수 있습니다. 비록 rlim_fd_max 파라미터를 조정함으로써 좀 더 많은 파일을 열 수 있지만, 파일을 수백개 열었을때에는 32-비트 프로세스의 가상메모리 공간이 문제가 될 수 있습니다. 프로세스가 가상 메모리의 한계에 다달았을때 stdioNot enough space 에러 를 발생시킵니다.

32-비트 어플리케이션을 실행하기 전에 다음의 두 단계를 거쳐서 확장 FILE 설비를 활성화 시킵니다:

  1. 쉘에서 파일 디스크립터의 최대 갯수를 증가시킴.
  2. 확장 FILE 설비를 Preload 시킴, /usr/lib/extendedFILE.so.1.

extendedFILE.so.1 은 라이브러리가 아니라 단순히 확장 FILE 설비를 활성화 시켜주는 공유 오브젝트 입니다.

아래에 ksh 에서 확장 FILE 설비를 활성화 하는 방법이 나와 있습니다:

% ulimit -n
256
% echo 'rlim_fd_max/D' | mdb -k | awk '{ print $2 }'
65536
% ulimit -n 65537
ksh: ulimit: exceeds allowable limit
% ulimit -n 65536
% ulimit -n
65536
% export LD_PRELOAD_32=/usr/lib/extendedFILE.so.1
% application [arg1 arg2 .. argn]

아래의 예제는 간단한 32-비트 프로세스가 확장 FILE 설비를 활성화 혹은 비활성화 시켰을때의 동작에 대해서 보여 줍니다. 테스트 케이스는 간단한 C 프로그램으로 fopen() 인터페이스를 통해서 65,536 개의 파일을 읽도록 시도 합니다.

% cat fopentestcase.c
#include 
#include 
#define NoOfFILES 65536
int main()
{
        char filename[10];
        FILE *fds[NoOfFILES];
        int i;
        for (i = 0; i < NoOfFILES; ++i)
        {
                sprintf (filename, "/tmp/%d.log", i);
                fds[i] = fopen(filename, "w");
                if (fds[i] == NULL)
                {
                        printf("\nNumber of open files = %d. " \
			   "fopen() failed with error:  ", i);
                        perror("");
                        exit(1);
                }
                else
                {
                        fprintf (fds[i], "some string");
                }
        }
        return (0);
}

기본 최대 갯수에 따른 오류를 볼 수 있습니다:

% cc -o fopentestcase fopentestcase.c
% ulimit -a | grep descriptors
nofiles(descriptors) 256
% ./fopentestcase
Number of open files = 253. fopen() failed with error: 
Too many open files

파일 디스크립터 한계를 증가 시키고 확장 FILE 설비를 활성화 시킨 다음 테스트 케이스를 다시 실행 시켜서 해결책이 잘 적용되는지 확인합니다.

% ulimit -n 5000
% ulimit -a | grep descriptors
nofiles(descriptors) 5000
% export LD_PRELOAD_32=/usr/lib/extendedFILE.so.1
% ./fopentestcase
Number of open files = 4996. fopen() failed with error:  
Too many open files
% ulimit -n 65536
% ulimit -a | grep descriptors
nofiles(descriptors) 65536
% ./fopentestcase
Number of open files = 65532. fopen() failed with error:
Too many open files

파일 디스크팁터가 하나가 부족함을 관찰할 수 있습니다 -- stdin, stdout, stderr 의 0, 1, 2 를 제외한. 확장 FILE 설비가 활성화 되면 파일 디스크립터 196은 예측불가능한 data 훼손을 최소화 하기 위해 기본적으로 비할당 됩니다. 다음 섹션 환경 변수 에서 좀 더 자세한 정보를 확인하시기 바랍니다.

pfiles 의 출력이 위의 설명을 증명하고 있습니다:

% pfiles `pgrep fopentestcase` | egrep "log|:"
 ...
 195: S_IFREG mode:0644 dev:102,7 ino:7380 uid:209044 ...
      /tmp/192.log
 197: S_IFREG mode:0644 dev:102,7 ino:7381 uid:209044 ...
      /tmp/193.log
 ...

환경 변수

두가지 환경 변수가 확장 FILE 설비를 조정하는데 사용됩니다: _STDIO_BADFD, _STDIO_BADFD_SIGNAL.

  • _STDIO_BADFD -- 이 변수는 3 에서 255 사이의 어떠한 종류의 정수 값도 지정될 수 있고 파일 디스크립터에서 비할당됩니다. 이 환경 변수를 설정하는 것은 소스 코드가 없는 써드 파티 라이브러리 같은 소프트웨어들의 예측할 수 없는 오류에 대한 보호 메카니즘을 제공하는 것입니다. 그럼으로써 예측불가능한 데이타 훼손을 방지할 수 있습니다. 이 환경변수가 없다면 기본적으로 196 이 사용되게 됩니다.

    여러분이 알다 시피 솔라리스 내의 오브젝트 코드들은 확장 FILE 설비가 존재하기 전에 빌드 된 것으로 8-비트 unsigned char 가 아닌 값을 받아 들이는 것을 기대하지 않을 것이고 확장 FILE 포인터를 다루는 법도 모릅니다. 이러한 이유로 이 값의 범위는 3에서 255 사이로 제한 됩니다. 그럼으로써 코드가 파일 디스크립터를 fileno(3C) 함수 대신 FILE -> _file 을 이용함으로써 실제로 확장 파일 디스크립터가 사용될때 fileno 함수의 에러를 방지할 수 있습니다.

  • _STDIO_BADFD_SIGNAL -- 이 환경 변수는 특정 코드가 비할당된 파일 디스크립터를 수정하려고 시도할때 발생할 시그널을 지정합니다. 이 환경변수는 유효한 시그널의 정수 혹은 문자열을 가지고 있습니다 signal.h(3HEAD) 를 통해서 유효한 시그널들을 확인하시기 바랍니다. 이 변수는 만약 확장 FILE 설비의 사용 도중에 어떠한 예외적인 케이스가 발생했을 경우 해달 시그널을 발생시키게 됩니다.기본 시그널은 SIGABRT 입니다.
이 해결책을 쓰지 말아야 할 때

만약 어플리케이션이 다음과 같은 작업을 할때 확장 FILE 설비를 활성화시키지 마시기 바랍니다:

  • FILE 구조체의 _file 멤버에 직접적으로 접근할때
  • 솔라리스 2.7 버전에서 부터 제거된 fileno() 매크로를 fileno(FILE) 함 수 대신 파일 디스크립터를 얻기 위해 사용할때

이 기능이 활성화 되면 255 이상의 파일 디스크립터는 어플리케이션의 임시 공간에 저장될 것입니다. 그리고 환경변수 _STDIO_BADFD 가 가지고 있는 비할당되거나 잘못된 파일 디스크립터는 FILE -> _file 멤버 필드에 저장될 것입니다. 어플리케이션의 부적절한 FILE -> _file 접근은 255 이상의 비할당된 잘못된 파일 디스크립터를 돌려 줄 것이고 이것은 예측불가능한 데이타 오류를 유발합니다.

또한 데이타 오류는 프로세스가 fileno(FILE) 함수에서 리턴 되는 값을 짤라버림으로써 발생할 수도 있습니다. 예를 들어 만약 16-비트 혹은 32-비트 int 값이 fileno() 함수에 의해 리턴 되서 8-비트 unsigned char 변수에 저장되면 짤림 현상이 발생하게 됩니다. 짤려져 버린 파일 디스크립터에 접근하게 되면 에러를 유발할 것입니다.

다음의 에러 메세지는 어플리케이션이 런타임시에 stdio 를 이용해 FILE 구조체의 파일 디스크립터 멤버 필드를 수정하고자 할때 발생합니다.

Application violated extended FILE safety mechanism.
Please read the man page for extendedFILE.
Aborting

이러한 메세지를 만나게 되면 어플리케이션에서 확장 FILE 설비의 사용을 중단해야 합니다. 만약 가능하다면 소스의 FILE -> _file 로직 대신에 fileno(FILE) 을 사용하도록 수정 합니다. 이 런타임 에러를 무시하는 것인 데이타 오류를 유발시킬 수 있습니다.

 
 
예제

다음의 간단한 예제는 두 환경 변수 _STDIO_BADFD, _STDIO_BADFD_SIGNAL 의 사용 법을 나타내고 있습니다. 또한 확장 FILE 설비의 안전 메카니즘을 위반함으로써 프로그램이 크래쉬 되는 것도 보여주고 있습니다.

확장 FILE 해결책이 적용되지 않은 솔라리스 시스템에서 다음의 코드를 컴파일 하고 라이브러리를 빌드 합니다. 주의: 패치 되지 않은 솔라리스10 3/05 버전에서 부터 11/06 버전의 배포판은 확장 FILE 해결책을 포함하고 있지 않습니다. 그러나 최신 커널 패치와 libc 패치를 적용함으로써 설치 할 수 있습니다. 패치와 버그 섹션을 참고하시기 바랍니다.

% cat thirdpartysrc.c
#include 
void manipulatefd (FILE *fptr)
{
        ;
        ;
        fprintf(stdout, "\n%s : manipulatefd(): " \
		"underlying file descriptor = %d\n", \
		__FILE__, fptr -> _file);
        fptr -> _file = 123;
        fprintf(fptr, "This call is gonna fail!\n");
        ;
        ;
}
% cc -G -o /tmp/libthirdparty.so thirdpartysrc.c

아래의 코드를 확장 FILE 설비가 설치 되어 있는 솔라리스에서 이전에 생성한 라이브러리를 이용해서 컴파일 하고 빌드합니다. :

% cat enableextfile.c
#include 
#include 
#define NoOfFiles 500
void manipulatefd(FILE *);
int main ()
{
        FILE *fptr;
        int i;
        for (i = 0; i < NoOfFiles; i++)
        {
                fptr = fopen("/tmp/enable_test.txt", "w");
                if (fptr == NULL)
                {
                        perror("fopen failed. ");
                        exit(1);
                }
                printf("\nfd = %d", fileno(fptr));
                if (fileno(fptr) % 400 == 0)
                {
                        manipulatefd(fptr);
                }
        }
        return(0);
}
% export LD_LIBRARY_PATH=/tmp:$LD_LIBRARY_PATH
% cc -o enableextfile -lthirdparty enableextfile.c

프로세스당 파일 디스크립터의 최대 갯수를 255 이상으로 증가시키고, 환경변수 _STDIO_BADFD_STDIO_BADFD_SIGNAL 를 설정하고 확장 FILE 설비를 /usr/lib/extendedFILE.so.1 을 reloading 시킴으로써 활성화 시킨 다음 실행파일을 실행시킵니다:

% ulimit -n
256
% ulimit -n 500
% ulimit -n
500
% export _STDIO_BADFD=196
% export _STDIO_BADFD_SIGNAL=SIGABRT
% export LD_PRELOAD_32=/usr/lib/extendedFILE.so.1
% ./enableextfile
fd = 3
fd = 4
fd = 5
...
...
fd = 398
fd = 399
fd = 400
thirdpartysrc.c : manipulatefd(): 
	underlying file descriptor = 196
Application violated extended FILE safety mechanism.
Please read the man page for extendedFILE.
Aborting
Abort(coredump)
% /usr/bin/pstack core
core 'core' of 10172:   ./enableextfile
 d1f28e65 _lwp_kill (1, 6) + 15
 d1ee2102 raise    (6) + 22
 d1ec0dad abort    (0, 80677e0, d1f60000, ...) + cd
 d1f01d54 _file_get (80677e0) + b4
 d1efeb21 _findbuf (80677e0) + 31
 d1ef2f16 _ndoprnt (d1f70344, 80471d4, 80677e0, 0) + 46
 d1ef669f fprintf  (80677e0, d1f70344) + 9f
 d1f702cb manipulatefd (80677e0) + 3b
 0805097f main     (1, 8047214, 804721c) + 9f
 0805084a _start   (1, 8047360, 0, 8047370, ... ) + 7a

만약 어플리케이션이 이전에 언급했던 패턴을 보여주지 않는 다면 위의 런타임 해결책의 장점을 취할 수 있습니다. 즉 솔라리스 7 혹은 그 이전 버전의 어플리케이션이라도 문제 없이 동작할 수 있습니다.

extendedFILE(5) 의 멘 페이지에서 좀 더 많은 예제를 보실 수 있습니다.