UNIX 시스템에서는 수많은 종류의 IO(input/output)을 다루기 위해 file을 다루는 것으로 통일하여 설계되었다.
UNIX에서 모든 IO는 device에 상관없이 file을 open/close/read/write 하는 것으로 제어 할 수 있다.
이는 /dev
에 속한 special file로 구현된다.
UNIX file은 byte의 나열이다.(sequence of byte, stream of byte)
open / close / read / write 를 사용해 모든 file을 다룰 수 있으며 이는 모든 입출력 device 또한 다룰 수 있음을 의미한다.
우선 open을 이용해 file을 열어야한다.
이때 file descriptor이 반환되어 파일이 열려있는 동안은 이 file descriptor을 사용해 파일을 가리킨다.
read 함수를 이용해 file에 적힌 데이터를 읽어 들일 수 있다.(input)
write 함수를 이용해 file에 데이터를 수정, 추가 할 수 있다.(output)
close함수로 열린 file을 닫는다.
파일을 여는 함수
int open(const char \*path, int flag)
: 기존 파일을 여는 함수
int open(const char \*path, int flag, mode_t mode)
: 새로운 파일을 생성하는 함수
path
로 파일 경로를 넘겨주게 되면 해당 파일을 열어 file-descriptor를 반환하는 함수 이다.
flag
parameter을 통해 여러 옵션을 부여할 수 있다
mode
를 이용해 권한 정보를 부여 할 수 있다.
O_RDONLY : 읽기 전용
O_WRONLY : 쓰기 전용
O_APPEND : file offset을 file 끝으로 이동 O_TRUNC : 파일 내용을 싹 지우고 열기 O_NOCTTY: prevents an opened device from becoming a controlling terminal
### 자동으로 열려있는 함수 표준 입출력 장치라고 불리는 장치들은 우리가 굳이 open() 하지 않아도 사용할 수 있다.STDIN_FILENO
으로 접근 가능하다.(실제 값 0)STDOUT_FILENO
로 접근 가능하다(실제 값 1)STDERR_FILENO
로 접근 가능하다.#include <fcntl.h>
#include <sys/stat.h>
file의 byte들을 읽어오는 함수이다.
ssize_t read(int files, void \* buf, size_t nbyte)
ssize_t
: int
값으로 읽어들인 문자의 개수를 반환한다.
files
: 대상 파일의 file descriptor값(open()의 return값)을 넣어주면 된다.
buf
: 읽어들인 글자들을 저장할 buffer이다. 배열의 형태로 전달한다.
nbyte
: 읽어들일 문자의 개수이다. 보통의 경우 buf 크기와 같다.
read 함수가 요청한 것보다 적은 수의 byte를 읽을 수도 있다.
보통은 buffer의 개수만큼 nbyte를 설정해 byte를 읽지만, nbyte에 못미치는 만큼의 글자만 들어올 수도 있다. 예를들어 파일의 끝에서는 buffer를 다 못채울만큼만 byte를 입력받을 확률이 높다.
만약 이런경우가 발생하더라도 read 함수는 정상적으로 작동했다고 본다.
#include <unistd.h>
file에 byte를 입력하기 위한 함수이다.
read함수와 거의 유사한 구조를 가지고 있다.
ssize_t write(int files, const void \*buf , size_t nbyte)
ssize_t
: 입력하는데 성공한 byte return
*buf
: buf의 byte들이 입력된다.
nbyte
: 만큼의 byte를 입력한다. 보통은 buf와 같은 개수.
read()와 마찬가지로 요청한 모든 byte가 입력에 성공 하지 않을 수 있다. 이 경우 입력에 성공한 byte 수를 return하고 함수를 정상 종료한다.
#include <unistd.h>
open
했던 file을 닫는 함수
int close(int filedescriptor)
filedescriptor만 넘기면 바로 닫을 수 있다.
(error발생하면 -1을 return)
#include <unistd.h>
file의 현재 수정위치를 가리키는 "file offset"을 조종하는 함수
off_t lseek(int files, off_t offset, int start_flag)
filedes : 수정할 filedescriptor
offset : int 형태의 offset. offset의 위치를 얼마나 움직일지 정한다. 사용할때 (off_t)를 이용해 형변환을 해야한다.
start_flag : offset을 움직일때 그 기준을 설정하는 함수.
ex) lseek(fd, (off_t)-100, SEKK_END)
-> 파일 끝에서 뒤로 100byte만큼 offset을 움직인다.
#include <sys/types.h>
#include <unistd.h>
지금까지는 file descriptor을 이용해 파일을 지정하고 함수들을 다뤘는데 C에서 제공하는 상용 파일 입출력 함수(fprintf, fscanf 등)에서는 file pointer
이라는 포인터로 FILE
자료형 이용해 file을 가리킨다.
이 차이에 대해 알아보자
우리가 file을 열면(open()) file descriptor Table이라는 process의 table안에 그 정보가 저장된다. 이 table은 kernel의 System file table을 가리키고 있고, 여기에서는 open된 file의 정보들을 저장하고 있다.(offset, open option 등등)
그리고 이 System file table이 In-memorry inode table을 또 가리키고 있어 이를 통해 file에 접근 가능해진다.(inode table은 file 하나 당 하나만 열린다.)
따라서 그동안 우리가 open()
의 return으로 받던 file descriptor은 file descriptor Table의 index였던 것이다.
(filedes가 int 형이었던 이유, STDIN_FILENO
류의 실제 값이 0,1,2였던 이유이다.)
그럼 file pointer는 무엇일까
FILE이라는 자료형에는 file descriptor와 buffer가 들어있다. 이를 가리키는 포인터가 file pointer이었던 것이다.
FILE = file descriptor + Buffer
따라서 printf, fprintf류의 함수들은 line단위의 buffer를 가진다. 이곳에 값이 저장되어 있다가 입력된다.