what is setvbuf & setbuf

상위 문저 what is file inner buffer

문제를 풀다보니,

C
    ret = setvbuf(stdin, buffer, course, size);

다음과 같은 부분을 사용해서 솔브 해야하는 문제가 나왔다. 그런데 setvbuf 는 항상 문제 초반에 초기화하는 부분에서 나오는 단골 함수이다.

C
void proc_init ()
{
  setvbuf (stdin, 0, 2, 0);
  setvbuf (stdout, 0, 2, 0);
  setvbuf (stderr, 0, 2, 0);
}

따라서, 이참에 확실히 알아보자setbuf setvbuf 함수의 함수원형은 다음과 같다.

C
#include <stdio.h>

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


#include <stdio.h>  // C++ 에서는 <cstdio>

void setbuf(FILE* stream, char* buffer);

함수의 기능을 요약하자면, 특정 파일 구조체의 내부버퍼를 설정하는

첫번째 인자에는 파일 구조체가 들어간다.

C
/* Standard streams.  */
extern FILE *stdin;		/* Standard input stream.  */
extern FILE *stdout;		/* Standard output stream.  */
extern FILE *stderr;		/* Standard error output stream.  */
/* C89/C99 say they're macros.  Make them happy.  */
#define stdin stdin
#define stdout stdout
#define stderr stderr

헷갈릴 수 있는데 stdin은 stdio.h에서 제공하는 파일 구조체를 나타내는 매크로이다.

두번째 인자는 둘다 buf의 주소를 가리키는데 NULL로 설정하냐 아니면 정말 버퍼의 주소를 넣냐에 따라 다르게 적용이 된다. 각각의 따라 모드가 다르게 적용이 되는데 하단의 설명을 하겠다.

모드의미(요약)언제 실제로 OS에 write/read가 나가나?주로 쓰이는 곳장점단점
Fully buffered (_IOFBF)버퍼가 “차면” 한 번에 처리버퍼가 가득 찼을 때, 또는 fflush/fclose/exit 등파일 I/O, 파이프로 리다이렉트된 stdout시스템콜 횟수 감소 → 빠름출력이 늦게 보일 수 있음(디버깅 불편)
Line buffered (_IOLBF)“줄 단위”로 처리개행(\n)이 보일 때, 또는 버퍼가 가득 찰 때, fflush터미널(stdout)이 대표적인터랙티브 출력에 좋음개행 없으면 출력이 안 보일 수 있음
Unbuffered (_IONBF)버퍼를 거의 안 씀(즉시 처리)호출할 때마다 최대한 바로 처리 (putc/printf 등)stderr가 대표적(빠른 로그)즉시 출력/입력 반응시스템콜 잦아져 느려질 수 있음
항목setbuf(FILE* stream, char* buf)setvbuf(FILE* stream, char* buf, int mode, size_t size)
제어 가능 범위간단한 ON/OFF 수준모드/크기/버퍼 위치까지 세밀 제어
모드 지정불가능(간접적으로만)가능: _IOFBF, _IOLBF, _IONBF
size 지정불가능(사실상 BUFSIZ 전제)가능(size 직접 지정)
buf가 NULL이면unbuffered로 설정_IONBF + buf NULL 식으로 명확히 제어
buf가 NULL이 아니면“fully buffered” 비슷하게 동작(구현 의존)_IOFBF/_IOLBF 등 원하는 방식으로
버퍼 메모리 책임사용자가 준 buf는 “사용자 소유”동일. buf를 주면 유지기간 보장 필요
반환값/에러처리반환값 없음성공 0, 실패 nonzero
언제 호출해야 안전?입출력 하기 전입출력 하기 전 (이미 I/O 한 스트림에 하면 UB 가능/구현 의존)

다음과 같다.

그렇다면 기본적인 내부버퍼의 위치는 어디일까?

파일에 있는 내부버퍼의 주 위치는 보통 heap이다.

C
pwndbg> x/gx 0x600c10
0x600c10 <stdin>:	0x00007ffff7dcda00
pwndbg> x/20gx 0x00007ffff7dcda00
0x7ffff7dcda00 <_IO_2_1_stdin_>:	0x00000000fbad2088	0x0000000000601261
0x7ffff7dcda10 <_IO_2_1_stdin_+16>:	0x000000000060129c	0x0000000000601260
0x7ffff7dcda20 <_IO_2_1_stdin_+32>:	0x0000000000601260	0x0000000000601260
0x7ffff7dcda30 <_IO_2_1_stdin_+48>:	0x0000000000601260	0x0000000000601260
0x7ffff7dcda40 <_IO_2_1_stdin_+64>:	0x0000000000602260	0x0000000000000000
0x7ffff7dcda50 <_IO_2_1_stdin_+80>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcda60 <_IO_2_1_stdin_+96>:	0x0000000000000000	0x0000000000000000
0x7ffff7dcda70 <_IO_2_1_stdin_+112>:	0x0000000000000000	0xffffffffffffffff
0x7ffff7dcda80 <_IO_2_1_stdin_+128>:	0x0000000000000000	0x00007ffff7dcf8d0
0x7ffff7dcda90 <_IO_2_1_stdin_+144>:	0xffffffffffffffff	0x0000000000000000
pwndbg> vmmap 0x0000000000601260
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
             Start                End Perm     Size Offset File
          0x601000           0x622000 rw-p    21000      0 [heap] +0x260
pwndbg> 

즉, 다음과 같이 내부적으로 heap 영역을 할당해서 사용한다.

물론 상황, 환경에 따라서 heap 대신 mmap 영역일 수도 있다. 또한, setbuf(setvbuf)로 완전히 원하는 위치로 바꿀 수도 있는셈

근데.. stderr 같은 unbuffered: FILE 내부 _shortbuf[1](=libc 데이터 영역)

라고 한다. 이를 통해 libc의 leak을 할 수도 있을듯?