재사용을 위한 컴파일된 객체 함수들의 집합
어플리케이션 개발을 할 때 이미 만들어져 있는 함수들을 편리하게 사용할 수 있도록 모아 놓은 것
ex) 그래픽 라이브러리, 수학 라이브러리 등
Linux에서는 일반적으로 /lib 또는 /usr/lib 디렉토리에 위치
사용자 프로그램에서 필요한 함수(객체)만 링크(link) 됨
컴파일 & 링크 과정 요약

함수의 복사본은 메모리에 하나만 있다 → 여러 프로세스간의 공유 → 메모리 절약
함수의 주소는 실행 시(런타임 run-time)에 결정 ➡︎ 이것을 동적 링크(dynamic linking) or 바인딩(binding)이라고 함
동적 링크를 위한 심볼 테이블이 메모리에 존재 → 메모리 오버헤드 발생
⭐심볼 테이블 (symbol table): OS는 각각의 라이브러리를 위한 정보들이 필요한데 이것을 심볼테이블이라 함, ex) printf는 몇 번째에 있고, scanf는 몇 번째에 있는지 ...
서버 시스템(server systems)에 유용
필요한 함수가 각 바이너리 프로그램에 직접 포함(link) 됨
⭐바이너리 프로그램 (binary program): 내가 만든 실행 파일
동일한 함수 코드가 여러 번 메모리에 중복 저장됨 → 메모리 오버헤드
임베디드 시스템(embedded systems)에 유용


: 입출력을 지원하는 라이브러리
✅#include <stdio.h>
표준 입출력(콘솔 & 파일)을 위한 기호(Symbol) 와 API 함수를 정의한 헤더 파일
⭐API: 응용 프로그램에서 사용할 수 있는 인터페이스와 사용하는 여러가지 심볼등을 정의
✅ File I/O with the standard I/O library

✅ I/O 장치는 특별한 파일로 매핑됨
➡︎ 콘솔(터미널), 프린터, 키보드 같은 I/O 장치들이 실제로는 파일처럼 다뤄진다는 의미
콘솔 터미널의 기본 파일: stdin , stdout, stderr
실행 시 자동으로 열려 있음
⭐파일 시스템: 파일을 저장하고 관리하는 OS의 핵심 기능
✅ FILE 포인터
표준 I/O 라이브러리에 의해 생성됨
FILE * 포인터를 통해 파일 접근
-포인터 안에는 파일에 대한 시스템 정보가 포함됨
✅ stdin, stdout, stderr
셸(shell)에 의해 자동으로 열려있는 FILE 포인터
사용자 프로그램이 상속받아 바로 사용 가능
✅ 시스템 콜 기반 I/O
운영체제는 FILE 포인터가 ❌, file descriptor(정수) 를 사용함
디스크립터는 정수 값이고,
ex)
0: stdin, 1: stdout, 2: stderr
사용자가 여는 파일의 디스크립터는 3부터 순차적으로 할당
보통 최대 1024개까지 열 수 있음
⭐Descriptor (파일 디스크립터): 운영체제가 사용하는 정수형 ID(번호)로,
파일, 소켓, 파이프 등 I/O 자원(Resource) 을 구분하고 관리하는 데 사용됨
✅ 표준 I/O 함수도 결국 시스템 콜 호출함
✅ 표준 I/O 라이브러리를 사용하는 이유
시스템 콜보다 편리하고 직관적임
버퍼링, 형식 지정 출력(formatting) 등의 기능 제공
✅ 변환 함수 2개 암기
int fileno(FILE *stream);
➡︎ FILE 포인터로부터 file descriptor(정수) 를 반환
FILE *fdopen(int fd, const char *mode);
➡︎ file descriptor를 받아서 FILE 포인터를 생성

: 사용자 레벨에서 버퍼를 사용해 입출력을 효율적으로 처리하는 방식,
즉, 데이터를 바로 보내거나 받지 않고, 임시 메모리(버퍼)에 모아뒀다가 한꺼번에 처리.
⭐ 버퍼(Buffer): 입출력(I/O) 데이터를 임시로 저장해두는 메모리 공간
사용자 프로그램(user process) 수준에서 라이브러리 함수가 버퍼 관리
➡︎ 목적: 시스템 콜 호출 횟수를 줄임 → 성능 향상
➡︎ 예시: 키보드 입력 중 "DEL" 키를 누르면 커널까지 전달되기 전에 버퍼에서만 처리 가능

✅ 1. Full Buffering
buffering를 모두 담는 경우
주로 디스크 파일 등에 사용
데이터가 일정량(수 KB 이상) 쌓이면 한꺼번에 시스템 콜 발생
시스템 콜 횟수를 효과적으로 줄임
fflush() 함수로 커널과 강제 동기화 가능 ➡︎fflush()는 강제적으로 데이터를 내보내는 함수
⭐ 동기화(Synchronization): 두 개 이상의 작업이 서로 간섭하지 않고 순서대로 제대로 작동하게 만드는 것
✅ 2. Line Buffering
주로 콘솔 입출력에 사용
엔터(newline)가 입력될 때 실제 I/O 발생
getchar() 문제(problem) ➡︎ 문자 하나를 눌러도 엔터 치기 전까지 전달되지 않음
✅ 3. Unbuffering (버퍼링 없음)
버퍼 ❌ 즉시 시스템 콜 호출
안정적 (전원 꺼져도 데이터 손실 적음)
보통 에러 메시지 출력(stderr) 에 사용됨
- 버퍼링 방식이 다르면 입출력 타이밍도 달라짐
- fflush()를 호출하지 않으면, 버퍼가 비워지지 않아 출력 안 보이는 문제 발생 가능
(Linux에서의 기본 설정)

✅ setbuf() 함수
void setbuf(FILE *stream, char *buf);
사용자 지정 버퍼 주소를 설정
buf != NULL: 일반적인 버퍼링 (full or line)
buf == NULL: 버퍼 없이 처리 (Unbuffered)
리턴값 ❌
✅ setvbuf() 함수
int setvbuf(FILE *stream, char *buf, int type, size_t size);
버퍼 주소뿐 아니라 버퍼링 유형과 크기까지 지정
type : 버퍼링 방식 설정
size : 버퍼 크기 (바이트 단위)
리턴값:
0: 성공
!= 0: 에러

setbuf() ➡︎ 간단한 버퍼 설정,
setvbuf() ➡︎ 버퍼링 방식 + 버퍼 크기까지 완벽 제어 가능!
: 운영체제가 자체적으로 메모리에 I/O 데이터 캐싱하는 것
➡︎ 디스크와 직접 입출력하는 대신, 커널 메모리(buffer cache, page cache)에 잠깐 저장함
⭐Cache (캐시): 자주 사용하는 데이터를 저장해두는 저장소 자체
⭐Caching (캐싱): 자주 사용하는 데이터를 빠른 저장소에 미리 저장해두고 필요할 때 빠르게 꺼내 쓰는 기술
✅ Page Cache (= Buffer Cache)
✅ 디스크에서 읽을 때
페이지 캐시에 먼저 접근
캐시에 없으면 ➡︎ 디스크에서 진짜 읽기 수행
✅ 디스크에 쓸 때

int fflush(FILE *stream);
➡︎ 라이브러리 버퍼의 데이터를 커널로 강제 전송 (동기화 목적)
리턴값:
0: 성공
EOF: 실패
⭐EOF: End Of File, 파일의 끝을 의미하는 특별한 정수 상수값 (보통 -1로 정의됨)
(주요 특징)
printf("something");
fflush(stdout); // 강제 출력!
printf()는 버퍼링 때문에 실제로 바로 출력되지 않을 수 있음
➡︎ 디버깅 중엔 fflush(stdout); 꼭 써야 출력이 즉시 반영됨
(디스크 I/O와 관련해서)
디스크는 블록 단위로 전송 (수 KB 단위)
fflush()는 라이브러리 버퍼 → 커널 페이지 캐시로 전송
디스크까지 동기화하려면?
➡︎ sync() 또는 fsync() 필요함
파일을 닫을 때(fclose() 호출 시), fflush()는 자동 호출됨

FILE *fopen(const char *filename, const char *type);
filename: 열 파일의 이름
type: 열기 모드 (r, w, a, …)
성공 시: FILE * 반환
실패 시: NULL 반환
(파일 열기 정리표)

⚠️ 시스템마다 동시에 열 수 있는 파일 수 제한 있음
FILE *freopen(const char *filename, const char *type, FILE *stream);
stream으로 연결된 기존 파일을 닫고, 같은 스트림을 재사용하여 새 파일 열기
반환값: 성공 시 새로운 FILE *, 실패 시 NULL
(동작 방식)
-stream에 연결된 파일을 먼저 닫음
새 파일을 열어 같은 stream(파일 포인터)로 재연결
기존 file descriptor도 그대로 재사용
(예제)
freopen("myfile.txt", "w", stdout);
printf("This sentence is redirected to a file.");
fclose(stdout);
stdout이 "myfile.txt"와 연결됨
printf() 출력이 화면 대신 파일로 저장됨
int fclose(FILE *stream);
반환값:
0: 정상 종료
EOF: 에러 발생
프로세스 정상 종료 ➡︎ 열려 있던 모든 파일 자동으로 닫힘
강제 종료 시 ➡︎ fclose() 에러 확인 못함 / 버퍼에 있는 데이터가 손실될 수 있음


















- fwrite()는 텍스트가 아닌 바이너리 파일 저장용 함수
- 플랫폼 차이(ex. endian, padding) 때문에 저장한 구조체를 다른 시스템에서 읽을 땐 문제가 생길 수도 있어
파일을 복사할 때 full buffering을 사용하는 예제이다. setvbuf() 함수를 사용하여 버퍼링 타입을 설정하고, fread()와 fwrite()를 통해 데이터를 블록 단위로 복사한다.




모든 열린 파일은 (읽기/쓰기) 오프셋을 가지고 있음 → 다음에 접근할 파일 위치를 나타냄
파일을 읽기/쓰기 모드로 열면 오프셋은 파일 시작 지점으로 설정됨
파일을 추가 모드(append)로 열면 오프셋은 파일 끝으로 설정됨
읽기/쓰기 중에는 오프셋이 자동으로 증가함














