본문 바로가기
소프트웨어개발

C, File I/O, open(), fopen() 그리고 dup()

by 보이드메인 2020. 3. 30.

모든 프로그래밍 언어에서 빠지지 않는 영역이 있다면, 그것은 바로 I/O 가 아닐까 한다.

예외상황도 많고, 알 수 없는 에러가 I/O 영역에서 가장 많이 발생한다.

시스템 성능차원에서도, I/O 로 인한 부하 무시못한다.

 

C 언어 에서 파일을 열고, 닫을때, 필수적으로 함수가 open()/close(), fopen()/fclose() 이다.

 

파일을 open() 함수를 사용하여  얻어낸 정수 값을 "파일 디스크립터"라고 한다면, 

fopen()으로 구한 FILE * 포인터로 얻어낸 포인터 값을 "파일 스트림 포인터"라고 할수 있다.

 

파일 디스크립터와 파일 스트림포인터. 어차피 파일을 다루기 위한 정보이지만,

주로 파일 I/O 에서는 Socket I/O 의 fd 처럼, 스트림(*포인터형) 보다는 디스크립터(숫자) 를 사용한다.

포인터 변수라 함은, OS 로부터 메모리를 할당받은 흔적이므로, 개발자로써는 사실 부담(?)스럽기도 하다.

 

하지만, 파일의 정보를 다루고자 한다면, 파일 디스크립터로는 구하기 어려운 정보가 있다. 

예를 들어 파일 크기를 구한다거나 파일 상세 속성 등이 그것이다.

 

스트림 포인터를 이용하면 간단하게 파일 크기를 구할 수 있다.

#include <stdio.h>

 

int main( void)

{

FILE *fp;

fp = fopen( "main.c", "r");

fseek( fp, 0L, SEEK_END);

printf( "file size=%d\n", ftell( fp));

fclose( fp);

}

fseek()나 ftell()은 모두 표준 입출력 함수로 파일 스트림 포인터를 가지고 있어야 사용 가능하다. 

 

그렇다면 open() 함수로 열어 사용했을 때에는 다시 같은 파일을 fopen() 으로 열기를 해야 할까?

다행이 fdopen() 함수를 사용하면 된다.

 

FILE *fdopen(int fildes, const char *mode);

저수준 함수를 사용하다가 표준 함수를 사용 필요시 편리하다.

 

#include <stdio.h>

#include <fcntl.h>

int main( void)

{

int fd;

FILE *fp;

fd = open( "main.c", O_RDONLY, 0644);

fp = fdopen( fd, "r");

fseek( fp, 0L, SEEK_END);

printf( "file size=%d\n", ftell( fp));

fclose( fp);

}

그런데 문제가 있습니다. 

fdopen으로 열기를 했다면 당연히 생성된 FILE * 에 자원을 제거하기 위해서라도 fclose()를 호출해야 하는데, 

 

그렇다면 open()으로 디스크립터를 구했으니 close()로 닫아 주어야 하는 문제가 있다. 

fclose()를 호출하여 파일을 닫았다면 open()으로 구한 디스크립터도 함께 닫히기 때문에 close()를 호출할 수 없다.

 

아래와 같이 함수로 구현해서 사용하기에는 불안하다. 함수 호출 후에 파일을 사용할 수 없기 때문이다.

 

#include <stdio.h>

#include <fcntl.h>

int file_size( int fd) // 파일 디스크립터로 파일 크기.

{

FILE *fp;

int sz_file;

fp = fdopen( fd, "r");

fseek( fp, 0L, SEEK_END);

sz_file = ftell( fp);

fclose( fp);

 

return( sz_file);

}

int main( void)

{

char buff[1024];

int fd;

fd = open( "main.c", O_RDONLY, 0644);

 

printf( "file size=%d\n", file_size( fd));

lseek( fd, 0, SEEK_SET);

while( 0 < read( fd, buff, 1024)) // 파일 내용을 출력

printf( "%s\n", buff);

close( fd);

}

 

실행하면, 파일 크기는 출력되어도 파일 내용은 출력되지 않습니다.

이미 file_size() 함수에서 fclose()를 호출하여 파일을 닫았기 때문이다.

 

이 문제를 해결하려면 디스크립터를 하나 더 복사해서, 복사본을 사용한다.

디스크립터도 dup()함수를 이용하면 복사본을 만들 수 있다.

 

int dup(int oldfd);

 

#include <stdio.h>

#include <fcntl.h>

#include <unistd.h>

 

int file_size( int fd) // 파일 디스크립터로 파일 크기

{

FILE *fp;

int sz_file;

 

fp = fdopen( fd, "r");

fseek( fp, 0L, SEEK_END);

sz_file = ftell( fp);

 

lseek( fd, 0, SEEK_SET); // 반드시 lseek()로 읽기 포인터를 처음으로

 

fclose( fp);

 

return( sz_file);

}

int main( void)

{

char buff[1024];

int fd;

 

fd = open( "main.c", O_RDONLY, 0644);

printf( "file size=%d\n", file_size( dup( fd)));

while( 0 < read( fd, buff, 1024)) // 파일 내용을 출력

printf( "%s\n", buff);

 

close( fd);

}

 

위 코드에서 file_size()를 호출할 때, 디스크립터의 복사본을 넘긴 것을 확인 할 수 있다.

파일 크기부터 파일 내용까지 이상없이 출력된다.

 

'소프트웨어개발' 카테고리의 다른 글

C 데몬프로세스 만들기  (0) 2020.10.16
opensource VoIP,IP-PBX : asterisk  (0) 2020.06.02
select,epoll,poll  (0) 2020.03.18
C++,참조연산  (0) 2020.03.18
[JAVA] DATA Type  (0) 2016.07.12

댓글