VFS & Filesystem Internals
서로 다른 수많은 파일 시스템을 하나의 표준 방식으로 다룰 수 있게 해주는 가상 파일 시스템(VFS) 계층과, 인덱스 노드(inode) 및 물리적 저장 구조를 다루는 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
operating-systems-system-mechanicsoperating-systemssystem-mechanicsmemory-managementvirtualization-physicsvfsfilesystem-internalsos-storage9 min read
1. Overview
VFS와 파일 시스템 내부(VFS & Filesystem Internals)는 리눅스가 하드디스크, 네트워크 소켓, USB 메모리, procfs 같은 가상 파일 시스템까지 open(), read(), write()라는 공통 인터페이스로 다루게 만드는 추상화 계층입니다.
학습자는 EXT4 파일 시스템이 파일을 메타데이터(Inode)와 데이터 블록(Data Block)으로 나누어 관리하는 방식을 살핍니다. 이어 디렉터리(Directory)가 이름과 Inode 번호를 매핑하는 자료구조라는 점, 디렉터리 엔트리(Dentry) 탐색 흐름, 시스템 크래시 이후 파일 시스템 일관성을 보호하는 저널링(Journaling) 구조를 이해합니다.
2. Scope & Boundaries
In-Scope
- VFS (가상 파일 시스템):
struct file,struct inode,struct dentry,struct super_block. 리눅스의 객체 지향적(Object-Oriented in C) 인터페이스. - 파일 시스템 레이아웃 (EXT4/XFS): 아이노드(Inode) 테이블, 데이터 블록(Data Block), 수퍼 블록(Superblock), 비트맵(Bitmap).
- 디렉토리 역학 (Namespace): 루트(
/)부터 시작하는 디렉토리 엔트리 탐색(Path Lookup), 하드 링크(Hard Link)와 심볼릭 링크(Soft Link)의 Inode 포인터 궤적. - 내결함성 (Crash Consistency): 저널링(Journaling / WAL - Write Ahead Log), FSCK(File System Check), 트랜잭션 롤백/리플레이.
Out-of-Scope
- 네트워크 소켓 버퍼 내부망: 소켓(TCP) 패킷이
read()로 얽혀 있긴 하지만 3-Way Handshake 같은 네트워크 스택 자체 03-02-04. Inter-Process Communication (IPC) 영역. - 물리적 배드 섹터와 RAID: 하드디스크 미디어 물리적 파괴나 디스크를 여러 개 묶는 컨트롤러 레이어 03-04-01. Disk, SSD & Magnetic Physics 영역.
Boundaries
- VFS vs. EXT4 (Abstract vs Concrete): VFS는
open(),read(),write(),close()같은 공통 인터페이스와 함수 포인터 구조를 정의합니다. EXT4나 XFS는 이 인터페이스를 구현해 실제 디스크 레이아웃에 맞게 동작합니다. VFS 덕분에 리눅스는 USB(FAT32)와 하드디스크(EXT4)를 같은 시스템 콜 경로로 다룰 수 있습니다.
3. Counterexample
- 아이노드 고갈의 함정 (The No-Space Illusion): 디스크 여유 공간이 500GB 남아 있어도, 1바이트짜리 텍스트 파일을 매우 많이 생성해 Inode가 모두 소진되면
No space left on device가 발생할 수 있습니다. 데이터 블록 용량은 남아 있어도 파일 메타데이터를 표현할 Inode가 부족하면 새 파일을 만들 수 없습니다. - 하드 링크와 삭제 오해 (Hard Link Deletion Trap): 원본 파일(Inode 10)에 하드 링크를 여러 개 만든 뒤
rm 원본을 실행해도, 다른 링크가 같은 Inode를 계속 참조하면 데이터 블록은 회수되지 않습니다. 하드 링크는 원본과 복사본을 구분하지 않고 동일한 Inode를 공유하며, 참조 카운트가0이 되어야 디스크 공간이 해제됩니다.
4. Prerequisites
- 페이지 캐시와 블록 I/O (Basic): 램(Page Cache)에 더티 버퍼가 남아있다가 블록 디바이스로 쏟아지는 지연 쓰기 물리 구조를 알아야 파일 시스템 저널링의 필요성을 깨닫습니다. (03-03-02 Page Cache)
- C 언어 구조체 포인터 (Recommended): VFS가
file_operations라는 구조체 안에 함수 포인터를 박아넣어 다형성(Polymorphism)을 구현하는 궤적을 이해해야 합니다.
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 공통 인터페이스와 가상 파일 시스템 (VFS)
- Why to Learn: 프로그램이 하드디스크(EXT4), USB 메모리(FAT32), 웹 소켓(TCP)을 읽을 때 서로 다른 구현 세부사항을 몰라도
read()같은 공통 인터페이스로 다룰 수 있는 이유를 이해하기 위해서입니다. - What to Learn:
- Concepts: VFS(Virtual File System), 4대 핵심 객체(Superblock, Inode, Dentry, File).
- Skills: 파일 디스크립터 테이블(File Descriptor Table),
file_operations함수 포인터 맵핑. - Tools: 리눅스 커널 소스
fs/read_write.c. - Trade-offs: VFS 계층을 두면 여러 장치를 파일처럼 다루는 통일성(Everything is a file)을 얻습니다. 대신 단순한 USB 파일 읽기도 [User
read()VFSsys_read()VFS 라우팅 FAT32fat_read()] 같은 함수 호출 경로를 거치므로 커널 오버헤드가 발생합니다.
- How to Learn:
- 1단계: 사용자 앱이
open()을 호출하면 VFS가struct file을 만들고, 그 안의read함수 포인터에 EXT4 파일 시스템의ext4_read()같은 구현 함수를 연결하는 다형성 바인딩을 확인합니다. - 2단계: 나중에 사용자가
read()를 호출하면 VFS가 연결된 함수 포인터(EXT4, FAT32, Socket 등)로 실행을 전달해 하위 구현이 작업을 수행하는 라우팅 흐름을 살핍니다.
- 1단계: 사용자 앱이
- Implement: 파이썬 클래스 상속과 다형성을 이용해 가상 VFS 엔진을 모사합니다.
VFS_File베이스 클래스를 상속받는Ext4_Driver,Socket_Driver클래스를 만들고,fd_table[3] = Socket_Driver()로 등록한 뒤fd_table[3].read()호출만으로 맞는 구현이 실행되도록 구성합니다.
Recommended
Core Topic 02: Inode와 Data Block 구조 (The Inode & Data Blocks)
- Why to Learn: 하드디스크에 저장된
test.txt가 하나의 연속 공간에만 놓이는 것이 아니라, 파일 메타데이터(Inode)와 여러 데이터 블록(Blocks)의 조합으로 관리된다는 구조를 이해하기 위해서입니다. - What to Learn:
- Concepts: 아이노드(Inode, Index Node), 데이터 블록(Data Block), 다이렉트/인다이렉트 포인터(Direct/Indirect Blocks).
- Skills: 파일 메타데이터(크기, 소유자, 권한, 수정시간) 파싱,
stat구조체. - Tools: 리눅스
ls -i,stat,df -i(Inode 사용량 확인). - Trade-offs: 데이터 블록을 연속으로 배치하면 순차 읽기 속도는 좋아지지만 삭제와 재할당이 반복될 때 외부 단편화가 생길 수 있습니다. Inode 포인터로 여러 블록을 연결하면 빈 공간 활용은 좋아지지만 포인터 탐색과 비연속 접근 비용이 늘 수 있습니다.
- How to Learn:
- 1단계: Inode: 파일 이름(
test.txt)은 디렉터리에 있고, Inode는 파일 크기, 소유자, 권한, 실제 데이터 블록 주소(예: 15번, 99번, 1024번) 같은 메타데이터를 담는 구조체임을 확인합니다. - 2단계: Indirect Blocks: 10GB 같은 큰 파일은 Inode 내부 포인터만으로 모든 블록을 가리키기 어렵습니다. Indirect Block을 통해 추가 주소 목록을 두고 이중/삼중 트리 구조로 데이터 블록을 참조하는 방식을 살핍니다.
- 1단계: Inode: 파일 이름(
- Implement: 100칸짜리 딕셔너리(Disk Blocks) 기반 파이썬 파일 시스템 모사.
Inode = {'size': 8KB, 'blocks': [14, 55]}를 할당하고 파일write()시 빈 블록(Bitmap 탐색)을 찾아 번호를blocks배열에 엮어 넣음. 나중에 10KB로 팽창(append)할 때 추가로 빈 블록 88번을 잡아 리스트에 박아 넣어 파편화(Fragmentation)된 데이터를 트리 포인터로 꿰매는 로직 덤프.
Practical
Core Topic 03: 디렉터리 엔트리와 링크 (Dentry & Linking)
- Why to Learn: 폴더(디렉터리)가 별도의 물리 상자가 아니라, 파일 이름 문자열과 Inode 번호를 연결하는 매핑 자료구조라는 점을 이해하기 위해서입니다.
- What to Learn:
- Concepts: 디렉토리 엔트리(Dentry), 디렉토리 데이터 구조, 하드 링크(Hard Link), 심볼릭 링크(Soft/Symbolic Link).
- Skills: 경로 탐색(Path Resolution,
/usr/bin/python), 참조 카운트(Reference Counting,i_nlink). - Tools: 리눅스
ln,ln -s,strace open. - Trade-offs: 하드 링크는 하나의 Inode에 여러 이름을 연결하므로 한 이름을 삭제해도 다른 링크가 남아 있으면 데이터가 유지됩니다. 다만 파일 시스템 경계를 넘기 어렵습니다. 심볼릭 링크는 다른 파티션의 경로도 가리킬 수 있지만, 원본 경로가 사라지면 Broken Link가 됩니다.
- How to Learn:
- 1단계: Dentry의 정체: 디렉터리 파일 내부가
["test.txt" -> Inode 101], ["my_dir" -> Inode 50]처럼 이름과 Inode 번호의 매핑으로 구성될 수 있음을 확인합니다. - 2단계: Path Lookup: 사용자가
cat /usr/bin/ls를 실행하면 커널은 루트(/)에서usr,bin,ls순서로 각 디렉터리 엔트리를 따라 Inode를 찾습니다.
- 1단계: Dentry의 정체: 디렉터리 파일 내부가
- Implement: 파이썬
Directory딕셔너리와Inode_Table을 분리해 구현합니다.Dir = {'readme.txt': 55}로 매핑하고,ln readme.txt copy.txt수행 시Dir['copy.txt'] = 55만 추가한 뒤Inode_Table[55].ref_count += 1을 올립니다. 원본 매핑을 삭제해도(del Dir['readme.txt']) 참조 카운트가 남으면 데이터가 유지되는 로그를 출력합니다.
Advanced
Core Topic 04: 저널링과 내결함성 (Journaling & Crash Consistency)
- Why to Learn: 파일 저장은 Inode 업데이트, 데이터 블록 복사, 디렉터리 엔트리 갱신처럼 여러 단계로 이루어집니다. 중간에 정전이 발생해도 파일 시스템 일관성을 복구할 수 있도록 저널링이 필요한 이유를 이해하기 위해서입니다.
- What to Learn:
- Concepts: 저널링(Journaling), WAL(Write-Ahead Logging), 트랜잭션 커밋(Transaction Commit), 복구 리플레이(Recovery Replay).
- Skills: FSCK(File System Check) 딜레이 타파, EXT4 저널 모드(Writeback, Ordered, Journal).
- Tools:
tune2fs -j(저널 옵션),dmesg(EXT4-fs recovery 로그). - Trade-offs: 저널링 없이 데이터를 바로 디스크에 쓰면 쓰기 경로는 단순하지만, 정전 발생 후 FSCK가 전체 디스크를 검사해야 할 수 있습니다. 저널링을 사용하면 로그를 보고 빠르게 롤백하거나 재실행할 수 있지만, 로그 영역과 원본 위치에 쓰는 이중 쓰기(Double Write) 비용이 발생할 수 있습니다.
- How to Learn:
- 1단계: 커널이 실제 데이터 위치를 수정하기 전에 "Inode 5번에 데이터 블록 99번을 할당한다"는 변경 기록(Log)을 Journal Area에 먼저 남기고 Commit 표시를 하는 Write-Ahead 흐름을 확인합니다.
- 2단계: 본 데이터 쓰기 중 크래시가 발생하면, 재부팅된 OS가 Journal을 확인해 미완성 트랜잭션을 롤백(Rollback)하거나 재실행(Redo)해 일관된 상태로 복구하는 과정을 살핍니다.
- Implement:
Commit_Log = []리스트를 둔 파일 I/O 스크립트를 작성합니다.f.write(데이터)수행 전Commit_Log에 트랜잭션 기록을 남기고, 원본 업데이트 전sys.exit(1)로 크래시를 모사합니다. 재실행 시Commit_Log를 스캔해 Commit 표시가 없는 트랜잭션을 롤백하고 Consistent State를 복구합니다.
7. Terminology
8. References
Primary
- [P2] SWEBOK v4.0 - Software Construction / Runtime Efficiency (Storage Management) — Performance context.
- [P1] CS2023 - OS/Operating System Principles (File Systems) — Core requirements.
Secondary
- [Understanding the Linux Kernel] Bovet & Cesati — Detailed VFS implementation.
- [Operating Systems: Three Easy Pieces] Remzi — The File System Abstraction.
Industry
- [Kernel.org: Overview of the Virtual File System] — Official Linux documentation.
- [Microsoft: Windows File System Architecture] — NTFS and Filter Drivers.
9. Final Checklist
Primary
- '모든 저장소는 파일이다(Everything is a file)'라는 철학이 VFS를 통해 어떻게 호출 구조로 구현되는지 설명 가능한가? (P1)
- 'inode'가 가지고 있는 정보 중 왜 '파일 이름'은 제외되는지, 디렉터리와의 관계를 통해 설명할 수 있는가? (P1)
Secondary
- '심볼릭 링크'와 '하드 링크'가 수정되었을 때 디스크 상의 inode 참조 카운트와 데이터 상태의 차이를 설명할 수 있는가?
- 대용량 파일 시스템 탐색 시, 왜 dentry 캐시가 시스템 전체의 UI 응답 속도에 영향을 주는지 인과관계를 도출할 수 있는가?
Industry
- 임베디드 리눅스 시스템 구축 시,
/proc과/sys를 활용하여 장치 드라이버의 상태를 실시간 모니터링하는 방안을 제안할 수 있는가? (SFIA) - 루트 파일시스템(Root FS)이 마운트되지 않았을 때 커널이 부팅 과정에서 겪게 되는 'Panic'의 원인을 기술하고 해결 절차를 마련할 수 있는가?