C 파일 입출력: 시스템 콜 vs 라이브러리 함수
C의 파일 입출력에는 시스템콜과 라이브러리 함수가 있다.
시스템 콜(System Call)은 커널(Kernel)에 직접 서비스를 요청하는 것을 말한다.
주로 하드웨어, 프로세스, 파일의 I/O등을 처리하며 프로그램은 사용자(User) 모드가 아닌 커널 모드로 실행된다. 시스템에 직접 접근하기 때문에 프로그래머가 로우레벨(Low-level)까지 제어할 수 있지만, 커널에 직접 접근하는 만큼 남용하면 성능 손실이 일어난다.
대표적인 시스템 콜 함수로는 open
, read
, write
가 있다.
라이브러리 함수는 시스템 콜 함수를 래핑(Wrapping)한 것이다.
함수 내부에서 메모리 할당/해제가 일어나고, 잦은 커널 호출로 인한 성능 손실을 줄이기 위해 입력을 모았다가 한 번에 커널에 요청하기도 한다.
대표적인 라이브러리 함수로는 fopen
, fscanf
, fprintf
가 있다.
시스템 콜인 open
은 파일 디스크립터(File Descriptor)을 반환하고, 라이브러리 함수인 fopen
은 파일 포인터(FILE*
)를 반환한다.
File Descriptor
파일 디스크립터는 아래와 같이 가져올 수 있다.
사용한 파일 디스크립터는 close
를 이용해 닫아줘야 한다.
#include <fcntl.h>
int main() {
int fd = open("hello.txt", O_RDONLY);
close(fd);
return 0;
}
파일을 읽고 수정할 때는 read
와 write
를 이용하면 된다. read
와 write
는 unistd.h
에 정의되어 있다.
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("hello.txt", O_RDWR);
char buf[101];
read(fd, buf, 100);
write(fd, buf, 100);
close(fd);
return 0;
}
시스템 콜 함수는 함수가 메모리를 직접 할당하지 못하기 때문에 파일을 읽을 때도 프로그래머가 buf
를 넣어줘야 한다.
FILE*
파일 포인터는 아래와 같이 가져올 수 있다.
사용한 파일 스트림(Stream)은 fclose
를 이용해 닫아줘야 한다.
#include <stdio.h>
int main() {
FILE* stream = fopen("hello.txt", "r+");
fclose(stream);
return 0;
}
파일을 읽고 수정할 때는 fscanf
와 fprintf
를 이용하면 된다.
#include <stdio.h>
int main() {
FILE* stream = fopen("hello.txt", "r+");
char buf[101];
fscanf(stream, "%s", buf);
fprintf(stream, "%s", buf);
fprintf(stream, "%s", buf);
fprintf(stream, "%s", buf);
fprintf(stream, "%s", buf);
fclose(stream);
return 0;
}
라이브러리 함수는 여러번 함수를 호출해도 파일 구조체 내부 버퍼에 수정사항을 담아 두었다가 한 번에 시스템 함수를 호출한다. 따라서 잦은 함수 호출에는 시스템 함수보다 빠르다.