C++/linux

(3) 버퍼 입출력

로파이 2022. 5. 10. 16:16

파일 포인터 FILE* (파일 스트림)로 표준 입출력 하기

 

파일 열기 fopen()

FILE* fopen(const char* path, const char* mode);

모드 mode

  • "r" : 읽기 모드
  • "w" : 쓰기 모드
  • "a" : 덧붙이기 모드
  • "r+", "w+", "a+": 읽기/쓰기 모드 모두 지원

성공 시 유효한 파일 포인터 반환, 실패 시 NULL을 반환.

 

파일 디스크립터를 통해 스트림 만들기 fdopen()

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

반환된 파일 스트림은 파일 디스크립터와 연결된 입출력을 제공한다. 파일 스트림을 닫으면 파일 디스크립터도 닫힌다.

 

스트림 닫기 fclose()

int fclose (FILE* stream);

성공 시 0, 실패 시 -1(EOF)를 반환

 

스트림에서 데이터 읽기

 

한 문자씩 읽기 fgetc

int fgetc(FILE *stream);

한 줄씩 읽기 fgets

char* fgets (char* str, int size, FILE* stream);

EOF나 개행 문자를 만나면 중단한다. 읽은 결과가 str에 저장된다.

 

바이너리 데이터 읽기 fread

size_t fread (void* buf, size_t size, size_t nr, FILE* stream);

buf 버퍼에 size단위로 nr개를 읽어서 반환한다. 읽어들인 원소 수가 반환된다. 

실패시 EOF(-1)가 반환되는데, 에러 상황인지 스트림의 끝인지 구분할 수 없다. 에러 여부는 ferror 혹은 EOF 확인으로 feof()를 사용해야한다. 

 

스트림에 데이터 쓰기

한 문자 쓰기 fputc

int fputc(int c, FILE* stream);

문자열을 기록하기 fputs

int fputs (const char* str, FILE* stream);

바이너리 데이터 쓰기 fwrite

size_t fwrite (void* buf, size_t size, size_t nr, FILE* stream);

buf가 가리키는 데이터에서 size 만큼 nr 개의 원소에 대하여 stream에 데이터를 쓴다.

 

스트림 탐색하기

int fseek(FILE* stream, long offset, int whence);

whence 값에 따른 탐색

SEEK_CUR : 현재로부터 offset만큼 파일 위치를 이동시킨다.

SEEK_END : 파일 끝으로부터 offset만큼 파일 위치를 이동시킨다.

SEEK_SET : 파일 위치를 offset으로 설정한다.

성공 시 0을 반환 실패시 -1

 

현재 스트림 위치 알아내기 ftell()

fseek의 경우 오프셋 위치가 반환되지 않는다. 따라서 다음 함수로 스트림 위치를 얻어야한다.

int ftell (FILE* stream):

 

사용자 버퍼를 커널 버퍼로 스트림 내보내기 fflush()

int fflush (FILE* stream);

stream에 쓰지 않은 데이터를 커널로 내보낸다. 사용자 버퍼에 있는 데이터를 커널 버퍼로 복사하기만 한다.

 

스트림에 에러 지시자가 설정되었는 지 확인 ferror()

fread의 결과로 -1이 반환되었을 때 에러 상황인지 확인하기 위해 사용한다.

int ferror(FILE* stream);

에러 지시자가 설정되어 있을 경우 0이 아닌 값을 반환하며 그렇지 않은 경우 0을 반환한다.

 

스트림이 파일 끝에 도달하였는 지 확인 feof()

int feof(FILE* stream);

EOF 지시자는 파일 끝에 도달하면 표준 입출력 인터페이스에서 설정한다.

 

에러 지시자와 EOF 지시자를 초기화 clearerr()

void clearerr(FILE* stream);

 

스트림에 연결된 파일 디스크립터 가져오기 fileno()

int fileno(FILE* stream);

fileno()가 성공하면 stream과 관련된 파일 디스크립터를 반환. 실패시 -1을 반환한다.

 

버퍼링 제어하기

표준 입출력은 사용자 버퍼링을 구현하고 버퍼의 유형과 크기를 다룰 수 있는 인터페이스를 제공한다.

int setvbuf(FILE* stream, char* buf, int mode, size_t size);

mode의 종류

  • _IONBF : 버퍼 미사용으로 항상 커널 버퍼에 데이터 쓰기가 이루어진다.
  • _IOLBF : 행 버퍼. 개행 문자가 나타나면 커널 버퍼로 데이터를 내보낸다.
  • _IOFBF : 블록 버퍼, 고정된 바이트 수의 블록 단위로 버퍼링을 수행한다. 파일에 적합한 버퍼 유형이다.


buf와 size를 무시하는 _IONBF 모드를 제외하고 size 바이트 크기의 버퍼를 가리키는 buf를 주어진 stream를 위한 버퍼로 사용한다. buf가 NULL 이라면, glibc가 자동적으로 지정한 크기 만큼 메모리를 할당한다.

 

스레드 세이프

표준 입출력 함수는 기본적으로 파일 스트림에 대하여 락을 포함한 동기화 기능을 포함한다.

int fputc(FILE* stream); // thread-safe

함수 단위의 락이 아니라 스트림에 대한 락을 사용함으로써 더 넓은 범위의 락을 구현할 수 있다.

void flockfile(FILE* stream);
void funlockfile(FILE* stream);

flockfile 함수는 해당 스트림의 락이 해제될 때까지 기다린다. 락을 얻게되면 락 카운터를 하나 증가시키고 해제시 감소시켜 반환한다.

 

항상 3개의 문자열이 한번에 stream에 같이 쓰는 것을 보장한다. 

flockfile(stream);

fputs("Data List in Shop", stream);
fputs("\t1) product1", stream);
fputs("\t2) product2", stream);

funlockfile(stream);

 

락을 사용하지 않는 표준 입출력 함수

함수 단위의 락을 포함하지 않는 표준 입출력 함수는 직접 동시성을 제어하여 최적화 가능성을 열어둔다.

int fgetc_unlocked(FILE* stream);
size_t fread_unlocked(void* buf,size_t size, size_t nr, FILE* stream);
int fputc_unlocked(FILE* stream);
size_t fwrite_unlocked(void* buf,size_t size, size_t nr, FILE* stream);
//...

 

표준 입출력과 사용자 버퍼링

많은 시스템 콜이 의심될 경우 버퍼링을 사용하여 시스템 콜 오버헤드를 줄이는 것을 해본다.

성능이 중요하며 모든 입출력이 정렬된 블록 경계에 맞춰 블록 크기 단위로 일어나도록 보장해야한다.

'C++ > linux' 카테고리의 다른 글

(6) Pthread 쓰레드 / PMutex 뮤텍스  (0) 2022.05.12
(5) 프로세스 관리  (0) 2022.05.11
(4) 고급 입출력 readv/writev/epoll/mmap  (0) 2022.05.10
(2) 다중 입출력 select와 poll  (0) 2022.05.10
(1) errno와 파일 입출력  (0) 2022.05.10