유닉스는 “모든 것은 파일”이라는 철학을 가지고있다. 따라서 존재하는 모든 리소스는 파일이다. 파일의 종류는 일반 파일부터 소켓, 파이프를 넘어 터미널까지도 모두 파일이다.
파일을 처리하기 위해 파일을 바라봐야하는데 이때 사용되는 개념인 파일 디스크립터를 정리해보자.
📌 파일 디스크립터(File Descriptor) 개요
- 정의: 프로세스에서 열린 파일을 식별하기 위해 커널이 부여하는 음수가 아닌 정수 값 (0, 1, 2, 3, …)
- 역할: 프로세스의 열린 파일 테이블(Open File Descriptor Table)의 인덱스
- 범위: 0 ~
OPEN_MAX (플랫폼별 상이)
📌 유닉스 철학 — “모든 것은 파일”
파일 디스크립터는 다음 모든 객체를 참조할 수 있음:
- 일반 파일(Regular File)
- 디렉토리(Directory)
- 소켓(Socket)
- 파이프(PIPE) / FIFO
- 블록 디바이스(Block Device)
- 문자 디바이스(Character Device)
- 터미널(Terminal)
📌 그림으로 보는 File Descriptor
FD 테이블의 각 항목은 FD 플래그와 파일 테이블로의 포인터를 가지고 있으며, 이 포인터를 이용하여 시스템의 파일을 참조 할 수 있게 된다.
💡 핵심
- 포인트는 해당 파일을 다루는 커널 내부 구조체의 주소를 가르킨다. 실제 I/O는 포인터가 가르키는 구조체에 등록된 연산 함수를 통해 이루어지며, 포인터를 이용해 대상 리소스를 찾아 OS 레벨에서 I/O가 수행된다.
![업로드중..]()
- 0번 FD → FD 테이블 항목
- 여기에 FD 플래그 + 파일 테이블 구조체 포인터가 있음.
- 파일 테이블 구조체(File Object)
- 해당 장치(여기선 키보드)에 대한 현재 상태, 연산 함수 포인터 등을 포함.
- 장치 드라이버를 통해 실제 하드웨어 I/O 가능.
- 키보드 장치
- 실제 하드웨어에서 입력을 읽어 OS 버퍼에 저장.
- read(0, buf, size) 호출 시:
- 0 인덱스 → FD 테이블에서 포인터 추적 → 파일 객체 → 드라이버 → 키보드 버퍼 읽음.
📌 기본 FD 번호와 의미
| 번호 | 목적 | POSIX 매크로 | stdio 스트림 |
|---|
| 0 | 표준 입력 | STDIN_FILENO | stdin |
| 1 | 표준 출력 | STDOUT_FILENO | stdout |
| 2 | 표준 에러 출력 | STDERR_FILENO | stderr |
- 프로그램 실행 시 셸의 FD 복사본을 상속받아 항상 열린 상태
📌 FD 동작 흐름 (open → read/write → close)
1. open(pathname, flags, mode)
- 커널이 해당 프로세스의 FD 테이블에서 비어있는 가장 작은 인덱스를 찾음
- 파일과 연결된 dentry 객체 생성
- dentry가 가리키는 inode 객체 생성(또는 로드)
- FD 테이블의 해당 인덱스에 이 파일 구조체를 연결
- FD 번호 반환
2. read(fd, buffer, count)
- FD 테이블[fd] → file 구조체 → dentry → inode
- inode를 통해 파일 내용 읽어서
buffer에 저장
3. write(fd, buffer, count)
- FD 테이블[fd] → file 구조체 → dentry → inode
- inode를 통해 파일에 데이터 기록
4. close(fd)
- FD 테이블에서 해당 엔트리 제거
- 참조 카운트 0이 되면 커널이 관련 자원 해제
📌 FD 관련 커널 내부 구조
files_struct
- 각 프로세스마다 존재
fd_array[]: 열린 파일들을 가리키는 배열
file 구조체
- 파일 오프셋, 플래그, 접근 모드, inode 참조
dentry 구조체
inode 구조체
- 파일 메타데이터(종류, 권한, 크기, 타임스탬프 등)
- 파일 연산 함수 포인터 집합
📌 같은 파일을 여러 FD로 열 경우
- 동일한 open file description(file 구조체)을 참조하면 → 파일 오프셋 공유
- 다른 open file description이면 → 오프셋 별도 유지
관련 글
참조 링크