scanf() inner buffer vs read() inner buffe
먼저, read() 의 버퍼
read(fd, buf, nbytes) 는 커널에 진입해서
파일 디스크립터(fd)가 가리키는 커널 내부 객체(inode, tty, socket 등)의 커널 버퍼 캐시에서 데이터를 꺼내
유저 공간의 buf에 직접 복사를 한다.
즉, read는 커널 ↔ 사용자 공간 사이의 경계에서 데이터를 옮기는 API ( 시스템콜 ) 이다. 커널 측 버퍼는 장치 드라이버나 VFS 레이어에 의해 유지되며, 프로그램이 직접 접근할 수 없다.
scanf() 의 버퍼
scanf는 내부에서 → fscanf(stdin, ...)이고, 내부적으로는 FILE *stdin을 사용한다.
FILE 객체(glibc의 _IO_FILE)는 유저 공간에 있는 inner buffer를 가지고 있음.
흐름:
처음 요청할 때 scanf가 내부 getc 류를 호출.
- 만약 stdin의 내부 버퍼가 비어 있다면 → 라이브러리가 read()를 호출해 대량(예: BUFSIZ = 8192바이트) 을 한 번에 가져옴
- 그 후 scanf는 내부 포인터(_IO_read_ptr.._IO_read_end) 를 움직이면서, 필요한 만큼만 파싱해서 메모리에 저장 • 즉, scanf는 read()를 자잘하게 여러 번 호출하지 않고, 자기만의 중간 버퍼에서 효율적으로 처리한다. • 남은 데이터는 유저 공간의 FILE 버퍼에 그대로 남아, 다음 호출 때 재사용
어떻게 보면 read 와 같은 시스템콜을 최대한 줄일려는 노력이기도 하다.
| 구분 | read() | scanf() (FILE 기반) |
|---|---|---|
| 위치 | 커널 공간 (OS 레벨) | 유저 공간 (C 라이브러리 내부) |
| 누가 관리? | 운영체제 커널 (VFS, 드라이버) | C 표준 라이브러리 (stdio) |
| 데이터 단위 | 호출 시 지정한 크기만큼 | 내부 버퍼 단위(보통 BUFSIZ)로 읽고 포인터 이동 |
| 시스템 콜 | 호출할 때마다 발생 | 버퍼가 빌 때만 발생 |
| 성능 | 작은 크기로 반복 호출 시 느림 | 버퍼링 덕분에 호출 횟수 줄어 빠름 |
| 기능 | 바이트 단순 복사 | 공백 스킵, 숫자 파싱 등 고수준 처리 |
참고로 FILE 기반 버퍼의 주요위치에 대해
운영체제 커널 (VFS, 드라이버) 여기서 좀더 자세히 들어가보자면,