본문 바로가기
리눅스

[리눅스 API] 파일 I/O 버퍼링

by 목가 2015. 8. 30.
반응형

파일 I/O 버퍼링


버퍼 크기가 I/O 시스템 호출 성능에 미치는 영향

 - 1억 바이트의 파일을 복사하는데 가장 적당한 크기의 버퍼 사이즈는 얼마일까? 

   실험결과 4096바이트 크기의 버퍼 사이즈일 때 가장 성능이 좋다.

 - 1억 바이트의 파일을 복사할 때, 대부분의 소요 시간은 디스크 읽는 시간이다.  


stdio 라이브러리 내의 버퍼링


#include <stdio.h>

int setvbuf(FILE *stream, char *buf, int mode, size_t size) : 성공하면 0을 리턴하고, 에러가 발생하면 0이 아닌 값을 리턴

 - stream 인자는 버퍼링이 수정돼야 하는 파일 스트림을 식별한다. 해당 스트림이 열리고 난 후에, setvbuf() 호출은 그 스트림에서 다른 stdio 함수를 호출하기 전에 실행돼야만 한다. setvbuf() 호출은 명시된 스트림의 향후 모든 stdio 오퍼레이션의 동작에 영향을 미친다. buf 가 NULL이 아니면 stream 의 버퍼로서 사용될 size바이트 메모리 블록을 가리킨다. buf가 NULL이면 stdio라이브러리는 자동적으로 stream에 사용될 버퍼를 할당한다. mode인자는 _IOBF(I/O를 버퍼링하지않는다), _IOLBF(라인 버퍼 I/O를 사용), _IOFBF(완전히 버퍼링된 I/O를 채용)

inf fflush(FILE *stream) : 성공하면 0을 리턴하고, 에러가 발생하면 EOF를 리턴한다. 

 - stream이 NULL이면, 모든 stdio 버퍼를 플러시한다. 입력 스트림에 적용될 수도 있다. 


파일 I/O의 커널 버퍼링 제어

 - 동기화된 I/O 데이터 무결성 완료 : 파일 데이터 갱신의 향후 추출을 진행하도록 허용하기 위해 충분한 정보를 전송하는 것을 보장한다. 읽기 수행에서는 요청된 파일 데이터가 프로세스로 전송 됐음을 의미한다. 쓰기 수행에서는 쓰기 요청에 명시된 데이터가 디스크로 전송됐고 이 데이터를 추출하는데 요구되는 모든 파일 메타데이터 역시 전송 완료됐음을 의미한다.

 - 동기화된 I/O 파일 무결성 완료 : 동기화된 I/O 데이터 무결성 완료의 집합니다. 이 모드가 갖는 I/O 완료의 차이점은, 파일 데이터의 차후 읽기 오페레이션에 관련이 없더라도 파일 갱신 동안 모든 갱신된 파일 메타데이터는 디스크로 전송된다는 것이다. 


#include <unistd.h>

int fsync(int fd) : 성공하면 0을 리턴하고, 에러가 발생하면 -1을 리턴

 - fsync() 호출은 버퍼링된 데이터를 야기하고, 열린 파일 디스크립터와 관련된 모든 메타데이터는 디스크로 플러시되게 한다. fsync() 호출은 그 파일에 동기화된 I/O 파일 무경성 완료 상태를 강제한다. 디스크 디바이스에 전송이 완료된 이후에만 리턴한다.

int fdatasync(int fd) : 성공하면 0을 리턴하고, 에러가 발생하면 -1을 리턴

 - fdatasync()를 사용하면 잠재적으로 fsync()에서 요구되는 두 가지 오퍼레이션에서 한 가지 오퍼레이션으로 디스크 동작의 수가 줄어든다. 예를들어, 파일 데이터가 변경됐지만 파일 크기는 변경되지 않은 경우 fdatasync() 호출은 데이터만 갱신한다. 반대로 fsync()호출은 메타데이터가 디스크로 전송되게 할 것이다. (디스크 I/O 오퍼레이션 감소효과)

void sync(void) 

 - sync()시스템 호출은 갱신된 파일 정보(데이터 블록, 포인터 블록, 메타데이터 등)를 포함하는 모든 커널 버퍼가 디스크로 플러시되게 한다. 리눅스 구현에서 sync()는 모든 데이터가 디스크 디바이스로 전송되고 난 후에만 리턴한다.  


모든 쓰기 동기화 O_SYNC 의 성능 영향

 fd = open(pathname, O_WRONLY  | O_SYNC) 

 - open() 호출 후에 해당 파일에 대한 모든 오퍼레이션이 자동적으로 파일 데이터와 메타데이터를 디스크로 플러시한다. 커널 버퍼의 플러시를 강제할 필요가 있을 경우, 파일을 열 때 O_SYNC 플래그를 사용하는 대신에, 큰 wirte() 버퍼 크기를 사용하거나 fsync()나 fdatasync()를 신중하게 간헐적으로 호출하도록 응용프로그램을 설계할 수 있는지 고려해야 한다. 


I/O 패턴에 대한 커널 조언

#include <fcntl.h>

int posix_fadvise(int fd, off_t offset, off_t len, int advice) : 성공하면 0을 리턴하고, 에러가 발생하면 에러 번호(양수)를 리턴

 - posix_fadvise() 시스템 호출은 파일 데이터에 접근하는 데 쓸 수 있는 패턴을 커널에게 알려주는 동작을 허용한다. 커널은 아마도 버퍼 캐시의 사용을 최적화하여 프로세스와 전체적인 시스템의 I/O 성능을 향상시키기 위해 이 함수를 사용할 것이다.

 - open() 으로 파일이나 디바이스를 열 때 O_DIRECT 플래그를 명시하면 개별적인 파일이나 블록 디바이스에서 직접 I/O를 실행할 수 있다. 


파일 I/O를 위한 라이브러리 함수와 시스템 호출의 혼합

#include <stdio.h>

int fileno(FILE *stream) : 성공하면 파일 디스크립터를 리턴하고, 에러가 발생하면 -1을 리턴

 - 주어진 스트림에 대해, fileno()는 해당되는 파일 디스크립터를 리턴한다. 이 파일 디스크립터는 read(), write(), dup(), fcntl() 같은 I/O 시스템 호출을 이용한 일반적인 방법으로 사용될 수 있다. 

FILE *fdopen(int fd, const char *mode) : 성공하면 파일 포인터를 리턴하고, 에러가 발생하면 NULL을 리턴

 - 주어진 파일 디스크립터로 I/O에 대한 이 디스크립터를 사용하는 해당 스트림을 생성한다. mode 인자는 foepn() 인자와 동일하다. 특히 보통 파일이 아닌 파일을 가리키는 디스크립터에 유용하다. (소켓이나 파이프)


반응형

'리눅스' 카테고리의 다른 글

dd 명령어  (0) 2015.11.19
gcc에서 문자열처리  (0) 2015.11.17
[Vi 편집기] 고급 기능  (0) 2015.08.22
[리눅스] 파일에서 내용 한번에 바꾸기  (0) 2015.08.20
[Vi 편집기] ^M 제거(없애기)  (0) 2015.08.20

댓글