표준 입출력 라이브러리 : standard I/O library 줄여서 stdio, libc의 큰 비중을 차지하는 만큼 중요한 라이브러리
1. stdio
1) 버퍼링
stdio는 커널 수준의 스트림을 쉽게 사용할 수 있는 인터페이스를 제공한다.
시스템 콜 read()는 스트림에서 우리가 지정한 buffer에 지정한 크기만큼 읽어 들인다. 반면, stdio는 독자적인 버퍼(buffer)를 사용한다.
버퍼란, 일시적으로 데이터를 저장하는 장소를 말한다. 그래서 시스템 콜 read()를 사용해서 적정한 크기의 데이터를 버퍼에 읽어 들이고, 프로그램에서 요구하는 만큼을 다시 반환해 준다. 이처럼 버퍼를 사용해서 데이터를 주고받는 것을 버퍼링(buffering)이라고 한다.
write(2) 또한 비슷하지만 몇가지 중요한 예외가 있다.
방향은 위 그림과 반대방향이며
먼저 스트림이 단말에 연결된 경우에는 버퍼가 가득 찰 때까지 기다리지 않고 개행('\n')을 만나는 시점에서 wrtie()를 실행한다.
이유는, 반대편에 모니터와 같은 단말이 있다면 사람이 출력을 보고 있을 가능성이 높기 때문이다.
버퍼가 가득 차기까지는 오랜 시간이 걸릴 수도 있기 때문에 적절한 순간에 바로 출력해주 는 것이 프로그램의 응답이 빨라지고 사용자의 사용성도 좋아진다.
두번째 예외는 스트림이 비버퍼링 모드(unbeffered mode)로 되어 있는 경우다. 비버퍼링 모드로 설정된 stdio 스트림에 데이터를 쓰면 버퍼링 없이 즉시 wrtie()가 수행된다. 이 모드는 setvbuf()로 설정할 수 있다. 구체적인 내용은 'man setvbuf'를 참고
세번째 예외는 표준 에러 출력에 해당하는 stderr에 대한 출력이다. stderr은 예외적으로 처음 부터 비버퍼링 모드다. 그 이유는 표준 에러 출력의 경우, 에러 메시지나 디버깅 정보를 출력하는데 사용되기 때문에 발생한 ㅅ시점에 바로 출력하는것이 바람직하기 때문이다.
2) FIFLE 타입
시스템 콜 레벨에서는 스트림을 지정하기 위해 파일 디스크립터라는 것을 사용했다.
한편, stdio 에서는 비슷한 역할을 위해 FILE 타입에 대한 포인터를 사용한다.
라이브러리를 사용하는 입장에서는 FILE 타입의 내부 구조를 몰라도 사용할 수 있다.
3) stdio의 표준 입출력
파일 디스크립터 | 정식 명칭 | stdio 변수명 | 의미 |
0 | STDIN_FILENO | stdin | 표준입력 |
1 | STDOUT_FILENO | stdout | 표준출력 |
2 | STDERR_FILENO | stderr | 표준에러출력 |
4) fopen(3)
표준 입출력 이외의 스트림에 대한 FILE을 여는 것도 물론 가능하다. 이떄는 fopen() 이라는 API를 사용한다. 이것은 시스템 콜 open()에 대응된다.
#include <stdio.h>
FILE *fopen(const char *path, const char *mode)
fopen()은 첫번째 인자로 path로 지정한 파일에 대한 스트림을 만들고, 그것을 관리하는 FILE 포인터를 반환한다.
만약 실패한 경우 NULL을 리턴, 원일을 나타내는 상수를 errno에 설정한다. 그리고 두 번째 인자는 mode에는 다음과 같은 옵션을 지정할 수 있다.
값 | 시스템 콜 open(2)에서 대응되는 모드 | 의미 |
"r" | O_RDONLY | 읽기용, 파일이 존재해야 한다. |
"w" | O_WRONLY, O_CREATE, O_TRUNC | 쓰기용, 파일이 없으면 새롭게 만든다. 있으면 크기를 0으로 새롭게 쓰기 시작한다. |
"a" | O_WRONLY, O_CREATE, O_APPEND | 이어 쓰기용, 파일이 없으면 새롭게 만들고, 있으면 파일의 끝에서부터 이어 쓴다. |
"r+" | O_RDWR | 읽고 쓰기용, 파일이 존재해야 한다. |
"w+" | O_RDWR, O_CREAT, O_TRUNC | 읽고 쓰기용, 파일이 없으면 새롭게 만들고, 있으면 크기를 -으로 하고 새롭게 쓰기 시작한다. |
"a+" | O_RDWR, O_CREAT, O_APPEND | 읽고 쓰기용, 파일이 없으면 새롭게 만들고, 있으면 파일의 끝에서부터 이어 쓴다. |
또한, 이외의 바이너리 파일을 나타내기 위해 'b'를 추가할 수 도 있다. 그러나 리눅스에서는 바이너리 파일과 텍스트 파일의 구분이 없기 때문에 지정해도 무시된다.
5) fclose(3)
시스템 콜 open()에 대응하는 API가 fopen()이라면 close()에 대응하는 APIㄴ느 fclose()다.
#incldue<stdio.h>
int fclose(FILE *stream);
fclose 첫번째 인자 stream이 가리키는 스트림을 닫는다.
성공적이면 0을 반환, 실패한경우 상수 EOF를 반환하고, 원인을 나타내는 숫자를 errno에 설정한다. EOF는 stdio.h에 정의된 매크로로 보통 -1이다.
'OS > 리눅스 기초' 카테고리의 다른 글
12. stdio의 동작 확인 (0) | 2019.10.10 |
---|---|
8. stdio로 cat 명령어 만들어보기 (0) | 2019.10.10 |
1. gcc로 컴파일하기 & 커맨드라인 인자 (0) | 2019.10.02 |
책소개 (0) | 2019.10.02 |