파일 디스크립터(File descriptor)란?
Unix 계열 OS에서 프로세스가 파일에 접근하고 다루기 위해 사용하는 개념이다. 시스템에서 파일을 열면 시스템은 해당 파일에 번호(정수값)를 부여하는데, 이 번호는 일종의 인덱스(index) 역할을 하게 된다. 따라서 프로세스는 이 파일 디스크립터 값을 통해 파일을 구분하고 해당 파일에 대한 여러 동작을 수행할 수 있게 된다.

위 그림은 파일 디스크립터를 간략하게 표현한 그림이다.
파일 디스크립터 0~2번은 각각 표준입력(stdin), 표준출력(stdout), 표준오류(stderr)가 기본적으로 할당되어 있다. 만약 프로세스에서 추가로 파일을 열게 되면, 이미 할당되어있는 파일 디스크립터 이후의 번호가 순차적으로 배정이 된다. (표준입출력을 임의로 건드리지 않았다면 3번부터 순차적으로 배정된다.)
표준입력(stdin), 표준출력(stdout), 표준오류(stderr)가 뭐냐하면
표준입력(stdin)은 사용자로부터 입력을 받고 표준출력(stdout)은 프로그램의 결과값을 출력하고 표준오류(stderr)는 프로그램 실행 시 발생한 오류를 출력하는 역할을 한다. 이 셋은 기본적으로 터미널을 통해 입출력을 받는다.
리눅스는 모든 것이 파일이다
파일 디스크립터가 열려 있는 파일에 접근하기 위해 사용한다고 했지만, 사실 우리가 흔히 알고 있는 파일에 접근할 때만 사용되는 것은 아니다. 리눅스를 포함한 Unix 계열 OS에서는 모든 것이 파일이다. 우리가 흔히 알고 있는 파일은 물론이고, 디렉토리, 소켓, 파이프, 심지어는 연결된 장치(디바이스)마저 파일로 되어 있다. 따라서 이런 파일들 역시 파일 디스크립터를 통해 관리할 수 있다. 예를 들어 프로세스 간 통신을 위해 생성한 파이프를 파일 디스크립터를 통해 관리하거나, 네트워크 통신을 위해 생성한 소켓을 파일 디스크립터를 통해 관리하는 경우가 있다.
파일을 다루는 기본적인 system call
이러한 파일 디스크립터를 이용해 파일을 다루는 가장 기본적인 시스템 콜 함수로는 open(), read(), write(), close() 가 있다. 하나하나 알아볼 것이다.
int open(const char* path, int oflag [, mode_t mode]);
open()은 path 경로에 위치한 파일을 oflag에 지정한 플래그 값에 따라 여는 함수다.
여기서 oflag는 파일을 열 때 옵션을 지정하는 값인데, 여기서 지정한 값에 따라 다른 동작을 수행한다.

mode는 파일을 생성할 경우, 생성하는 파일의 접근권한을 지정하는 인자로, 파일을 생성하지 않는 경우는 생략이 가능하다.
이렇게 실행된 open()은 연 파일의 파일 디스크립터 값을 반환하는데, 이는 연 파일을 제어할 때 사용된다.
ssize_t read(int fd, void *buf, size_t nbytes);
read()는 fd라는 파일 디스크립터에 지정된 파일의 내용을 읽어오는 함수이다.
fd는 읽어올 파일 디스크립터를 의미한다.
buf는 읽어온 내용을 저장할 버퍼(임시공간)을 의미한다.
nbytes는 읽어올 파일 길이(바이트)를 의미한다.
요약하면 fd라는 파일 디스크립터에 지정된 파일을 nbytes만큼 buf에 저장한다는 의미다.
이렇게 실행된 read()는 실제 읽어온 바이트 수를 반환한다.
ssize_t write(int fd, void *buf, size_t nbytes);
write()는 파일에 데이터를 쓰는 함수이다.
바로 위의 read()와 인자의 의미는 동일하지만, 차이점이 하나 존재한다.
read()는 fd 파일의 내용을 buf로 읽어오지만, write()는 buf의 내용을 fd 파일에 쓴다는 점이다.
그리고 write()는 read()처럼 실행 후 실제로 파일에 쓴 바이트 수를 반환한다.
int close(int fd);
close()는 열려있는 파일을 닫는 함수이다.
이를 실행하게 되면, 해당 파일 디스크립터에 지정된 파일을 닫고, 파일 디스크립터 테이블에서도 해당 파일 디스크립터의 정보를 삭제한다. 이후에 파일을 열 때 해당 번호의 파일 디스크립터를 사용할 수 있게 되는 것이다.
물론 0~2번에 기본적으로 할당되어 있는 표준 입출력 장치에도 사용할 수 있다. 예를 들어, 만약 0번 파일 디스크립터를 닫게 되면, 표준입력(stdin) 파일을 닫게 되므로, 해당 프로세스는 입력을 받을 수 없게 된다.