Computer Science%/System

입출력 버퍼 사용하기

ch4rli3kop 2018. 11. 8. 01:27
반응형


버퍼(buffer)란?


흔히 우리가 변수로 사용하는 buffer의 origin인데, 실제 데이터가 실행 함수에 의해 입출력을 진행할 때, 바로 입력 혹은 출력이 되는 것이 아니라 버퍼(buffer)라는 공간에 저장이 된 이후에 동작하게 된다. 이는 데이터를 효율적으로 처리하기 위함인데, 매번 데이터를 처리하는 것보다 일정 크기만큼 데이터를 모아둔 뒤 처리하는 것이 CPU 사용 횟수나 메모리 접근 횟수를 줄일 수 있다.(system call file I/O와 다르게 FILE *을 이용한 file I/O는 중간 buffer를 이용하여 system call 횟수를 줄임으로써 자원을 효율적으로 사용한다.) 
그러나 이러한 버퍼의 기능때문에 다양한 예기치 않은 문제를 만날 수 있는데, 대부분 버퍼가 완전히 비워지지 않아 문제가 발생한다. 버퍼 안에 개행문자('\n')가 남아있어 이 후의 입력에 영향을 미치던가, 버퍼의 설정때문에 출력버퍼가 바로 비워지지않아 프로그램 흐름에 문제가 발생한다던가 하는 것들이 바로 그 예시들이다. 나 역시 그런한 문제가 발생했고, 아래 내가 이 글을 쓰게된 원인이 있다. 아래의 경우는 출력버퍼를 제대로 비워주지 않아서 문제가 발생한 경우이다.

출력버퍼 문제 발생 예시

#include<stdio.h>

void printmenu(){
    puts("hello");
    printf(">> ");
}

int main(){
    char str[256];
    printmenu();
    read(0,str,0x100);
    return 0;
}



아무튼 이런 입출력버퍼때문에, 위 경우와 같이 프로그램 개발에 예기치 않은 문제를 만날 수 있으므로, 아래의 setvbuf() 함수를 통해, 입출력 버퍼의 설정을 바꾸는 방법을 알아보도록 하자.


setvbuf() 함수


스트림 버퍼링 방식과 버퍼의 크기를 설정하는 함수로, 다음과 같다.
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int type, size_t size);

/*
stream
stdin(표준입력, 0)
stdout(표준출력, 1)
stderr(표준에러, 2) 
혹은 open 등으로 생성한 파일 포인터 등이 있다.

buf
입출력 버퍼로 사용할 메모리. NULL이면 malloc을 통해 내부적으로 버퍼공간을 생성한다.

type
_IOFBF(0)    Full buffering 버퍼가 가득차면 버퍼를 비운다. buf를 시작으로 size만큼의 크기를 가진 버퍼를 사용한다. 
_IOLBF(1)    Line buffering 행 단위 버퍼링이 사용된다. 개행문자('\n')가 사용되거나, 버퍼가 가득 차거나, 입력이 요청되면 버퍼를 비운다.
_IONBF(2)    No buffering버퍼를 사용하지 않는다. buf와 size는 무시한다.

size
버퍼의 크기를 설정한다.
*/


Return 값
성공하면 0을 리턴하고, 유효하지 않은 값이 매개변수 리스트에 지정되거나 요청을 수행할 수 없는 경우 0이 아닌 값을 리턴한다.



입출력 버퍼 다루기 예시


setvbuf()
setvbuf(stdout, 0, 0, 10);  // 출력 버퍼의 크기를 10으로 설정. 버퍼가 가득 찬 뒤 출력된다.

setvbuf(stdout, 0, 2, 0);  // CTF 문제에서 볼 수 있는 사용. 버퍼를 없앤다.
setvbuf(stdin, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);


/*표준 스트림이 아닌 파일 포인터에서 사용할 경우*/
#include <stdio.h>
#define  BUF_SIZE  1024
char buf[BUF_SIZE];
FILE *stream1, *stream2;
int main(void)
{
   stream1 = fopen("myfile1.dat", "r");
   stream2 = fopen("myfile2.dat", "r");
   /* stream1 uses a user-assigned buffer of BUF_SIZE bytes */
   if (setvbuf(stream1, buf, _IOFBF, sizeof(buf)) != 0)
      printf("Incorrect type or size of buffer\n");
   /* stream2 is unbuffered                                  */
   if (setvbuf(stream2, NULL, _IONBF, 0) != 0)
      printf("Incorrect type or size of buffer\n");
/*  This is a program fragment and not a complete function example  */
}


버퍼 비우기 예시
while (getchar() != '\n');}  // 입력 버퍼에서 문자를 하나씩 꺼내며, 개행문자('\n')를 꺼내면 중단하게 된다.

fflush(stdout);  // 출력 버퍼를 강제로 비운다.
fflush(stdin);  // 윈도우에서만 동작 가능. 입력 버퍼를 강제로 비운다. C 언어 표준에서 정의되지 않은 행동이기에 컴파일러마다 다르게 동작한다.


참고 : 


반응형