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을 할 수도 있을듯?