
리눅스에서 모든 객체(하드웨어 포함)는 '파일'로 취급되며, 우리는 fd(정수 번호) 라는 번호표를 통해 커널 안의 실제 파일과 소통한다.
1. 핵심 철학: Everything is a file
- 추상화: 하드디스크 파일뿐만 아니라 키보드, 모니터, 프린터, 네트워크 소켓까지 전부 파일로 간주합니다.
- 상호작용: 따라서 장치를 제어하는 방법도 복잡한 명령어가 아닌, 단순한
Read(읽기)와 Write(쓰기)로 통일됩니다.
2. 연결 고리: File Descriptor (fd)
- 정체:
int (비음수 정수). (예: 3, 4, 5...)
- 역할: 사용자 프로그램(User)이 커널(Kernel) 내부에 열려 있는 실제 파일 객체(메타데이터)를 가리키는 인덱스(참조 번호)입니다.
- 공유: 커널은 파일을 관리하고, 사용자에게는 이 번호표(fd)만 건네줍니다. 사용자는 이 번호만 알면 됩니다.
3. 시스템 프로그래밍의 표준 흐름 (Lifecycle)
리눅스 프로그래밍의 90%는 이 과정을 따릅니다.
- Open: "파일 열어줘" → 커널이 확인 후
fd 번호 발급.
- Access: "이
fd에 써줘" → 커널이 fd를 보고 실제 파일에 기록.
- Close: "이
fd 다 썼어" → 커널이 fd 회수 및 리소스 정리.
표준 파일 디스크립터 (Standard FD)
프로그램이 실행되자마자 기본적으로 할당받는 3가지 fd가 있습니다.
- 0: 표준 입력 (Stdin) - 키보드
- 1: 표준 출력 (Stdout) - 모니터
- 2: 표준 에러 (Stderr) - 모니터 (에러용)
"Inode는 파일의 '주민등록증(고유 식별자)', fd는 현재 프로세스가 발급받은 '대기 번호표(접근 핸들)'.”
| 비교 항목 | Inode (Index Node) | fd (File Descriptor) |
|---|
| 정체 | 파일 그 자체 (메타데이터) | 파일을 다루기 위한 번호 (핸들) |
| 위치 | 디스크 (물리적 저장) | 프로세스 메모리 (커널 관리 테이블) |
| 유효 범위 | 파일 시스템 전체에서 유일 | 해당 프로세스 안에서만 유일 |
| 수명 | 파일이 삭제될 때까지 영구적 | close() 하거나 프로세스 죽으면 사라짐 |
| 포함 정보 | 권한, 소유자, 크기, 데이터 위치 | 현재 읽기/쓰기 위치(Offset), 접근 모드 |
연결 구조 (Kernel Internal)
리눅스 커널은 이 둘을 3단계로 연결합니다.
- 프로세스 (fd 테이블):
fd 3은 단순한 인덱스(번호)일 뿐입니다.
- 오픈 파일 테이블 (File Table Entry):
fd가 가리키는 곳. "누가, 어떻게 열었고, 어디까지 읽었나(Offset)"를 저장.
- Inode 테이블 (Vnode): 실제 디스크의 물리적 위치와 권한 정보.
관계: 여러 프로세스가 같은 파일을 열면?
- fd: 서로 다름 (A프로세스: 3, B프로세스: 4).
- Open File Table: 서로 다름 (각자 읽는 위치가 다르니까).
- Inode: 하나를 공유함 (물리적 파일은 하나니까).
Regular 파일
리눅스 파일은 '구조가 없는 바이트의 나열(Stream)'이며, 이름은 껍데기일 뿐 실체는 Inode이다.
1. 파일의 본질 (Byte Stream)
- 정의: 바이트(Byte)들이 선형으로 쭉 늘어선 배열.
- 특징:
- No Structure: 리눅스 커널은 파일의 내용(이미지인지, 텍스트인지)을 모릅니다. 그냥 0과 1의 덩어리로 취급합니다.
- 자유도: 파일 내부는 어떤 값이든 가질 수 있습니다.
2. 파일 접근과 오프셋 (File Offset)
- 개념: 현재 파일의 "어디를 읽고/쓰고 있나"를 가리키는 위치 커서.
- 동작:
- 파일 열면 0에서 시작.
- 읽거나(
read) 쓰면(write) 그만큼 숫자가 증가.
- 주의 (덮어쓰기):
- 파일 중간에 데이터를 쓰면 끼워넣기(Insert)가 아니라 덮어쓰기(Overwrite)가 됩니다.
- 파일 크기 확장은 보통 맨 끝(End of File)에 쓸 때 일어납니다.
3. 동시성 및 공유 (Concurrency)
- 다중 오픈: 하나의 파일을 여러 프로세스가(혹은 한 프로세스가 여러 번) 동시에
open() 할 수 있음.
- 고유성: 열 때마다 새로운 *
fd가 발급됨. (서로 다른 오프셋을 가짐).
- 동기화: 커널은 교통정리를 안 해줍니다. A가 쓰고 있는데 B가 덮어써도 막지 않습니다. (사용자 공간에서
flock 등으로 직접 동기화 필수).
4. 식별자 (Inode vs Filename)
- 파일명: 사용자가 보기 편하게 붙인 별명(껍데기).
- Inode (i-number): 파일 시스템이 파일을 관리하는 진짜 주민등록번호(실체).
- 모든 파일 접근은 내부적으로
파일명 → Inode 번호 변환을 거쳐 일어납니다.
Inode
Inode는 파일의 실체(메타데이터+데이터 위치)이며, 파일명은 이 Inode를 가리키는 문패(Link)일 뿐이다.
- 정의: 파일에 대한 모든 정보(메타데이터)를 담고 있는 핵심 자료구조.
- 포함하는 것:
- 속성: 파일 크기, 소유자(UID), 권한(Permission), 시간(Timestamps).
- 위치 정보: 실제 데이터가 디스크 어디(Block)에 저장되어 있는지 가리키는 포인터.
- 포함하지 않는 것: 파일 이름(Filename). (이름은 디렉터리가 관리함).
- 존재 형태:
- 물리적: 디스크의 특정 영역(Inode Table)에 저장된 객체.
- 논리적: 리눅스 커널 메모리에 로드된
struct inode 객체.
2. 파일명과 Inode의 관계 (The Link)
- 파일명: 파일 그 자체가 아니라, Inode 번호를 가리키는 별명(Link)에 불과합니다.
- 디렉터리 엔트리: 디렉터리는
(파일 이름, Inode 번호) 쌍을 저장하는 리스트입니다.
- 하드 링크(Hard Link):
- 여러 개의 파일명(별명)이 동일한 Inode 번호를 가리킬 수 있습니다.
- 파일명은 달라도 실체(Inode)는 하나이므로 데이터도 같습니다.
3. 파일 접근 흐름 (Lookup)
- 요청: 사용자가
/home/pi/test.txt 읽기 요청.
- 검색: 커널이 디렉터리를 뒤져
test.txt라는 이름에 매핑된 Inode 번호를 찾음.
- 로딩: 해당 번호의 Inode를 메모리로 읽어옴.
- 접근: Inode 안에 적힌 권한을 확인하고, 데이터 블록 위치를 찾아 데이터를 읽음.

Directory와Link
"디렉터리는 파일을 담는 상자가 아니라, '파일 이름'과 'Inode 번호'를 짝지어 놓은 전화번호부(Mapping Table)이다.”
1. 디렉터리와 링크의 본질
- 디렉터리 (Directory):
- 역할: 사용자에게 사람이 읽을 수 있는 이름을 제공하고, 커널이 쓰는 Inode 번호로 변환(Mapping)해주는 특수 파일.
- 구조: 내부적으로는 단순히
{파일 이름 : Inode 번호} 쌍(Link)들의 리스트만 담고 있음.
- 목적: 복잡한 Inode 번호를 직접 외우는 번거로움을 없애고 접근 제어(보안)를 도움.
- 링크 (Link):
- 디렉터리 안에 기록된 [파일 이름] + [Inode]의 매핑 한 줄.
2. 경로 이름 해석 (Pathname Resolution)
커널이 /home/pi/file.c라는 요청을 받았을 때의 동작 과정입니다.
"이름 → 디렉터리 뒤지기 → Inode 획득 → 파일 접근"
- 디렉터리 열기: 해당 이름이 포함된 디렉터리(예:
/home)를 읽음.
- 검색: 디렉터리 리스트에서 파일 이름(pi)과 일치하는 항목을 찾음.
- Inode 추출: 그 이름 옆에 적힌 Inode 번호를 알아냄.
- 반복/접근: 최종 파일에 도달할 때까지 이 과정을 반복하여 실제 Inode(데이터)에 접근.
3. 경로의 종류 (Pathname)
| Absolute Path
(절대 경로) | 루트(/)부터 시작하는 완전한 주소 (Fully Qualified). | /home/pi/lab/ch02/access.c |
| Relative Path
(상대 경로) | 현재 위치(pwd)를 기준으로 찾아가는 주소. | lab/ch02/access.c |
4. 디렉터리 조작 (Operation)
디렉터리도 파일(dentry)이지만, 구조가 깨지면 파일 시스템 전체가 꼬이기 때문에 일반적인 쓰기(write)가 금지되어 있습니다.
- 일반 조작:
open(), write() 불가능.
- 전용 시스템 콜:
- 생성:
mkdir()
- 삭제:
rmdir() (단, 비어있을 때만 가능. rm -r은 재귀적으로 비우고 지우는 유틸리티 기능)
Hard link vs. Symbolic link
"Hard Link는 '동일한 파일의 또 다른 이름(별명)', Symbolic Link는 '원본 위치를 가리키는 바로가기(Shortcut)'."
1. Hard Link (하드 링크)
"하나의 Inode를 여러 이름이 공유하는 것."
- 구조: 원본 파일과 동일한 Inode 번호를 가짐.
- 특징:
- 원본과 링크의 구분이 없음 (완벽하게 동등한 파일).
rm으로 원본을 지워도, 링크가 남아있다면 데이터는 삭제되지 않음 (Inode Reference Count가 0이 되어야 삭제됨).
- 제약: 같은 파일 시스템(파티션) 내에서만 생성 가능. (Inode 번호는 파티션마다 따로 관리되므로).
2. Symbolic Link (심볼릭 링크 / Soft Link)
"다른 파일을 가리키는 경로(Path)를 담은 별도의 파일."
- 구조: 자신만의 고유한 Inode를 가짐.
- 내용: 실제 데이터가 아니라, 원본 파일이 있는 "경로 문자열"만 저장하고 있음.
- 특징:
- 윈도우의 '바로가기 아이콘'과 동일.
- 파일 시스템(파티션)을 넘나들 수 있음.
- 오버헤드: 원본을 찾기 위해 경로를 해석하는 과정이 추가되므로 Hard Link보다 느림.
- 제약: 원본 파일을 지우면 링크는 "깨진 링크(Broken Link)"가 되어 사용 불가.
3. 비교 요약
| 구분 | Hard Link | Symbolic Link |
|---|
| Inode 번호 | 원본과 같음 (공유) | 다름 (새로 생성) |
| 데이터 | 원본 데이터 직접 가리킴 | 원본의 경로(Path) 저장 |
| 파티션 이동 | 불가능 (동일 파티션만) | 가능 (어디든 참조 가능) |
| 원본 삭제 시 | 파일 살아있음 (접근 가능) | 링크 깨짐 (접근 불가) |
| 속도 | 빠름 (직접 접근) | 약간 느림 (경로 해석 필요) |
| 명령어 | ln 원본 링크명 | ln -s 원본 링크명 |
특수 파일(Special files)
"리눅스는 하드웨어 장치(키보드, 디스크)나 통신 채널(소켓)도 전부 '파일'로 취급하여 관리한다.”
1. 장치 파일 (Device Files) - /dev
하드웨어를 제어하기 위한 인터페이스입니다.
| 구분 | Block Device (블록 장치) | Character Device (문자 장치) |
|---|
| 데이터 구조 | 바이트 배열 (Array) | 선형 큐 (Queue) |
| 접근 방식 | Random Access (순서 무관, 임의 접근) | Sequential Access (순서대로, 스트림) |
| 동작 특징 | 데이터를 블록 단위로 버퍼링하여 전송 | 데이터를 한 바이트씩 흐르는 대로 처리 |
| 예시 | 하드디스크, SSD, USB 메모리 | 키보드, 마우스, 시리얼 포트, 프린터 |
| 비고 | lseek으로 위치 이동 가능 | 이동 불가. (읽을 게 없으면 EOF 또는 대기) |
2. IPC(통신) 파일 - 프로세스 간 대화 수단
① Named Pipe (FIFO)
- 정의: 파일 시스템에 이름(파일명)을 가지고 존재하는 파이프.
- 특징:
- 부모-자식 관계가 아닌 전혀 다른 프로세스끼리도 통신 가능.
mkfifo 명령어로 생성.
- 한쪽이 읽기 전까지 쓰기 작업이 블로킹(대기)됨.
② Socket (소켓)
- 정의: 네트워크 또는 로컬 통신을 위한 진보된 IPC의 끝점(Endpoint).
- 유형:
- Unix Domain Socket: 동일 머신 내에서 가장 빠른 통신 (파일 경로 사용).
- Internet Socket: 다른 머신(네트워크) 간 통신 (IP 주소 + Port 번호 필요).
- 특징: 양방향 통신이 가능하며, 현대 서버 프로그래밍의 핵심.