ch18. file system ext2 ext3
Ext2와 Ext3,
파일은 디스크에 어떻게 저장되는가
VFS가 파일 쓰기를 명령할 때, 실제 디스크의 어느 블록에 어떤 데이터가 기록될까요? Ext2는 블록 그룹, 비트맵, 아이노드, 그리고 간접 주소 지정(indirect addressing)을 통해 이 질문에 명쾌하게 답합니다. 한편 Ext3는 동일한 물리적 구조를 유지한 채 '저널(journal)'이라는 작업 일지를 추가하여 장애 발생 시 복구 속도를 획기적으로 높였습니다.
큰 그림: 파일은 디스크에 어떻게 배치되는가WHY EXT2 & EXT3
우리가 write() 시스템 콜을 호출하여 파일에 문자 하나를 기록했다고 가정해 봅시다. 이 문자는 결국 디스크의 어디에 저장될까요? 커널은 "이 파일의 N번째 바이트가 디스크의 M번째 블록에 위치한다"는 사실을 어떻게 기억하고 관리할까요?
지금까지 파일시스템을 다룰 때는 주로 VFS, 아이노드, 덴트리, 페이지 캐시 등 "운영체제가 파일을 바라보는 논리적 관점"에 집중했습니다. 이번 장에서는 시각을 넓혀 디스크의 물리적 구조를 살펴봅니다. 오랜 기간 리눅스의 표준으로 활약했던 Ext2와, 그 구조적 기반 위에 저널링을 도입한 Ext3가 이번 강의의 핵심 주제입니다.
Ext2가 '체계적으로 정돈된 창고'라면, Ext3는 그 창고 입구에 '작업 일지'를 비치해 둔 것과 같습니다. 창고의 기본 골조는 바꾸지 않되, 언제 어떤 물품을 옮겼는지 기록을 남겨 예기치 못한 사고에 대비하는 셈입니다.
이번 강의는 크게 네 가지 주제로 나뉩니다. 첫째, Ext2의 구조적 효율성과 견고성. 둘째, 슈퍼블록, 그룹 디스크립터, 비트맵, 아이노드 테이블의 디스크 물리적 배치. 셋째, 마운트 이후 메모리 상에서의 캐싱 메커니즘. 마지막으로 디스크 공간의 할당 및 해제 알고리즘과 Ext3 저널링을 통한 복구 최적화 원리입니다.
가장 중요한 핵심 질문은 다음과 같습니다. "파일 하나를 생성하고 내용을 기록한 뒤 삭제하기까지, 파일시스템은 어떤 메타데이터를 수정하고, 어떤 비트를 조작하며, 실제 디스크의 어느 블록을 찾아가는가?" 이 흐름을 따라가면 파일시스템 전체의 작동 원리가 선명하게 보일 것입니다.
핵심 O/X 퀴즈
1. Ext2와 Ext3를 학습하는 것은 VFS의 추상적인 개념이 실제 디스크 구조로 어떻게 매핑되는지 이해하는 과정이다.
2. Ext3는 Ext2와 호환성이 전혀 없는 완전히 새로운 파일시스템이다.
3. 파일의 논리적 오프셋을 물리적 디스크 블록 위치로 연결하는 원리를 이해하는 것이 이번 장의 핵심이다.
Ext2의 등장과 효율성: 오랜 기간 사랑받은 이유EXT2 DESIGN CHOICES
초기 리눅스는 MINIX 파일시스템을 기반으로 출발했으며, 이후 Ext(Extended Filesystem)가 등장했지만 성능 면에서 다소 아쉬움이 있었습니다. 1994년에 도입된 Ext2는 혁신적인 설계와 뛰어난 성능, 견고성을 바탕으로 Ext3와 더불어 오랫동안 리눅스를 대표하는 파일시스템으로 활약했습니다.
- 블록 크기의 유연성
- 파일시스템 생성 시 1,024바이트부터 4,096바이트 사이에서 선택이 가능하여, 저장할 파일들의 평균 크기에 맞춰 내부 단편화와 I/O 전송 효율을 최적화할 수 있습니다.
- 아이노드 개수 조절
- 대용량 파일이 주를 이룰 때는 아이노드를 적게, 작은 파일이 밀집된 환경에서는 아이노드를 넉넉하게 할당하도록 관리자가 직접 설정할 수 있습니다.
- 블록 그룹(Block Group) 구조
- 디스크 파티션을 여러 블록 그룹으로 분할하고, 관련된 아이노드와 데이터 블록을 최대한 동일한 그룹 내에 배치하여 디스크 헤드의 이동 시간(Seek time)을 최소화합니다.
- 데이터 블록 사전 할당
- 파일 크기가 증가할 것으로 예상될 때 인접한 블록을 미리 확보해 두어 데이터 단편화(Fragmentation)를 사전에 방지합니다.
- 빠른 심볼릭 링크
- 경로명 길이가 60자 이하라면 별도의 데이터 블록을 할당하지 않고 아이노드 내부 공간에 직접 저장하여 빠른 접근이 가능합니다.
블록 그룹의 개념은 "같은 프로젝트를 수행하는 팀원들을 사무실의 같은 층에 배치하는 것"과 같습니다. 자주 참조되는 아이노드와 데이터 블록을 물리적으로 멀리 떨어뜨려 놓으면 디스크 탐색 비용이 기하급수적으로 증가하지만, 같은 블록 그룹 내에 모아두면 접근 속도가 크게 향상됩니다.
핵심 O/X 퀴즈
1. 용량이 작은 파일이 많은 환경에서는 블록 크기를 작게 설정하는 것이 내부 단편화를 줄이는 데 유리하다.
2. Ext2는 디스크의 마모를 방지하기 위해 파일의 데이터 블록을 항상 디스크 전역에 균등하게 분산시켜 저장한다.
3. 경로명이 짧은 심볼릭 링크의 내용은 데이터 블록이 아닌 아이노드 내부에 직접 저장될 수 있다.
Ext2의 견고성: 속도와 안전성의 조화ROBUSTNESS & CONSISTENCY
파일시스템은 갑작스러운 전원 차단이나 커널 패닉 등 예기치 않은 시스템 장애에 항상 대비해야 합니다. 장애 발생 순간, 데이터가 갱신되다 만 '중간 상태(Inconsistent state)'가 파일시스템 전체에 얼마나 치명적인 영향을 미치는지가 시스템 견고성의 핵심 척도입니다.
Ext2는 메타데이터를 갱신할 때 작업의 순서를 매우 엄격하게 통제합니다. 예를 들어 새로운 하드 링크를 생성할 때, 반드시 디스크 아이노드의 링크 카운터를 먼저 증가시킨 후 디렉터리에 새로운 엔트리를 추가합니다. 만약 순서를 반대로 했을 때 시스템이 다운된다면, 디렉터리가 가리키는 아이노드가 유효하지 않아 심각한 데이터 손상으로 이어질 수 있기 때문입니다.
- mount count
- 파일시스템이 정상적으로 마운트된 누적 횟수입니다.
- max mount count
- 마운트 횟수가 이 임계치를 초과하면 자동으로 무결성 검사(fsck)를 강제 수행합니다.
- last check time
- e2fsck를 통해 파일시스템 검사가 마지막으로 수행된 시각입니다.
- check interval
- 마지막 검사 이후 이 간격이 지나면 시스템 부팅 시 자동 검사가 진행됩니다.
- filesystem state
- 정상 종료(Clean unmount) 여부를 기록하여, 비정상 종료 시 즉각적인 복구 절차를 밟을 수 있게 합니다.
또한 Ext2는 파일의 수정을 원천 차단하는 불변 파일(Immutable file) 속성과, 데이터의 추가 기록만 허용하는 추가 전용 파일(Append-only file) 속성을 지원합니다. 추가 전용 파일은 기존 기록을 보존해야 하는 시스템 로그 파일 등을 관리할 때 매우 유용합니다.
다만, Ext2의 가장 큰 구조적 결핍은 저널링(Journaling) 기능의 부재였습니다. 커널 개발자들은 Ext2의 핵심 구조를 무리하게 갈아엎는 대신, 기존 레이아웃과 완벽히 호환되면서도 저널링 기능만을 정교하게 덧붙인 Ext3를 탄생시켰습니다.
핵심 O/X 퀴즈
1. Ext2는 파일 갱신 시 순서를 전략적으로 제어하여 시스템 크래시 후 발생할 수 있는 데이터 불일치를 최소화한다.
2. 저널링은 원래 Ext2 파일시스템의 공식 핵심 기능 중 하나이다.
3. e2fsck 도구는 비정상적으로 마운트 해제된 파일시스템의 일관성을 점검하고 복구하는 데 사용된다.
디스크 레이아웃: 블록 그룹(Block Group)의 배열DISK LAYOUT & BLOCK GROUPS
디스크 파티션의 첫 번째 블록은 파일시스템이 직접 관리하지 않습니다. 이 공간은 시스템 부팅을 위한 '파티션 부트 섹터(Partition Boot Sector)'로 예약되어 있으며, Ext2의 본격적인 관리는 그다음 블록부터 시작되어 여러 개의 블록 그룹으로 나뉩니다.
모든 블록 그룹은 기본적으로 동일한 내부 구조를 갖습니다. 각 그룹 내에는 슈퍼블록과 그룹 디스크립터의 복사본, 데이터 블록 비트맵, 아이노드 비트맵, 아이노드 테이블, 그리고 실제 파일 데이터가 저장되는 데이터 블록들이 순서대로 자리 잡습니다.
모든 블록 그룹에 슈퍼블록과 그룹 디스크립터가 복제되어 있지만, 시스템 운영 중 커널이 주로 참조하는 것은 '블록 그룹 0'에 위치한 메인 데이터입니다. 나머지 블록 그룹의 복사본들은 일종의 '백업 보험'입니다. 물리적 배드 섹터 등으로 메인 슈퍼블록이 손상되었을 때, e2fsck가 다른 그룹에 보관된 복사본을 활용하여 파일시스템을 기적처럼 복구해 낼 수 있습니다.
- 결정 원리
- 하나의 블록 비트맵은 반드시 단일 블록 안에 온전히 들어가야 합니다. 비트 1개가 블록 1개의 상태를 나타내므로, 블록 크기가 b바이트라면 하나의 그룹은 최대 8 × b개의 블록만을 포함할 수 있습니다.
- 4KB 블록의 예시
- 4KB 블록 환경에서는 하나의 비트맵이 최대 32,768(32K)개의 블록 상태를 표현할 수 있습니다. 이를 용량으로 환산하면 128MB이며, 따라서 32GB 파티션을 구성하려면 약 256개의 블록 그룹이 필요하게 됩니다.
핵심 O/X 퀴즈
1. Ext2 파티션의 최상단 첫 번째 블록은 일반 파일의 데이터 저장용으로 사용될 수 없다.
2. 블록 그룹 구조를 채택한 주된 이유는 연관된 메타데이터와 데이터를 물리적으로 인접시켜 탐색 시간(Seek time)을 최소화하기 위함이다.
3. 커널은 성능 향상을 위해 모든 블록 그룹에 존재하는 슈퍼블록 복사본을 라운드 로빈 방식으로 번갈아 가며 참조한다.
파일시스템의 핵심 장부: 슈퍼블록, 그룹 디스크립터, 비트맵METADATA STRUCTURES
파일시스템의 전반적인 상태를 기록하는 장부 중 가장 중요한 것은 단연 슈퍼블록(Superblock)입니다. 슈퍼블록은 해당 파일시스템의 '디지털 신분증'이자 통제 센터 역할을 합니다.
- 전체 아이노드 및 블록 수
- 파일시스템이 관리하는 총 물리적 용량에 대한 기본 제원입니다.
- 유휴(Free) 공간 정보
- 현재 즉시 할당 가능한 유휴 블록과 아이노드의 개수를 나타냅니다.
- s_log_block_size
- 블록 크기를 1,024바이트를 기준으로 한 2의 거듭제곱 형태로 표현합니다. (0 = 1KB, 1 = 2KB, 2 = 4KB)
- 예약 블록(Reserved blocks)
- 일반 사용자로 인해 디스크 용량이 100% 가득 차더라도, 관리자(root)가 장애를 조치할 수 있도록 시스템 차원에서 남겨둔 여유 공간입니다.
- 매직 시그니처(Magic signature)
- 해당 파티션이 Ext2 포맷임을 증명하는 고유 식별 코드(0xEF53)입니다.
두 번째 핵심 장부는 그룹 디스크립터(Group Descriptor)입니다. 모든 블록 그룹은 각자의 디스크립터를 보유하며, 여기에는 해당 그룹 내 데이터 블록 비트맵의 위치, 아이노드 비트맵의 위치, 아이노드 테이블의 시작점, 그리고 현재 남아있는 유휴 블록 및 아이노드의 개수 등이 상세히 기록되어 있습니다.
마지막 장부는 블록의 할당 상태를 직관적으로 나타내는 비트맵(Bitmap)입니다. 비트 값이 0이면 사용 가능(Free), 1이면 사용 중(Used)을 의미합니다.
결과적으로 파일시스템의 공간 관리는 "비트맵의 특정 비트를 토글(Toggle)하고 잔여 카운터를 갱신하는 일련의 과정"으로 귀결됩니다. 완전히 새로운 파일 하나를 생성한다는 것은, 디스크의 아이노드 비트맵에서 0을 찾아 1로 뒤집고, 슈퍼블록의 유휴 아이노드 카운터를 1 감소시킨 뒤, 관련 캐시 버퍼를 Dirty 상태로 마킹하는 정교한 작업입니다.
핵심 O/X 퀴즈
1. 슈퍼블록은 단일 파일의 권한 정보가 아닌, 파일시스템 전체의 통계와 상태를 요약한 전역 메타데이터이다.
2. Ext2 비트맵 구조에서 일반적으로 비트 값 0은 해당 블록이 사용 중임을 의미한다.
3. 파일이나 디렉터리를 새로 생성할 때, 시스템은 그룹 디스크립터를 참조하여 유휴 공간이 넉넉한 블록 그룹을 탐색한다.
아이노드 테이블과 디스크 아이노드: 파일의 메타데이터 보관소DISK INODE STRUCTURE
각 블록 그룹 내에 존재하는 아이노드 테이블은 디스크 아이노드 구조체들이 촘촘히 도열해 있는 연속된 블록 덩어리입니다. Ext2에서 디스크 아이노드 1개의 크기는 기본적으로 128바이트로 고정되어 있습니다. 즉, 4KB 블록 하나에는 정확히 32개의 아이노드가 빈틈없이 저장됩니다.
- i_mode
- 파일의 유형(정규 파일, 디렉터리, 소켓 등)과 권한(퍼미션) 정보를 담고 있습니다.
- i_uid / i_gid
- 파일 소유자의 사용자 ID와 그룹 ID입니다.
- i_size
- 사용자 공간(User-space)의 프로그램이 인식하는 파일의 논리적 바이트 길이입니다.
- i_atime / i_ctime / i_mtime
- 파일 접근 시각, 아이노드 상태 변경 시각, 파일 데이터 수정 시각을 각각 기록합니다.
- i_links_count
- 이 아이노드를 가리키는 유효한 디렉터리 엔트리(하드 링크)의 개수입니다.
- i_blocks
- 실제로 디스크에 할당된 블록의 누적 크기를 512바이트 단위 섹터 수로 표현합니다.
- i_block[15]
- 데이터 블록의 주소를 가리키는 포인터 배열입니다. 앞의 12개는 다이렉트 포인터이며, 나머지 3개는 인다이렉트 포인터로 활용됩니다.
여기서 i_size와 i_blocks의 차이를 명확히 구분하는 것이 대단히 중요합니다. i_size는 애플리케이션 관점에서의 논리적 파일 크기이고, i_blocks는 파일시스템이 실제로 물리 디스크를 점유하고 있는 용량입니다. 데이터가 비어 있는 '파일 홀(File hole)'이 존재할 경우, 논리적 크기가 물리적 크기보다 훨씬 클 수 있습니다.
Ext2 시스템의 뛰어난 설계 중 하나는, 아이노드 번호를 물리적 디스크 주소로 변환할 때 별도의 룩업 테이블(Mapping table)이 필요하지 않다는 점입니다. 커널은 단순한 산술 연산만으로 특정 아이노드가 어느 블록 그룹의 아이노드 테이블 중 몇 번째 오프셋에 위치하는지 즉각적으로 도출해 냅니다.
핵심 O/X 퀴즈
1. Ext2 디스크 아이노드는 가변적인 크기를 가지며, 파일 이름이 길수록 아이노드의 크기도 커진다.
2. `i_size` 필드는 0이 아닌데 `i_blocks` 필드는 0인 상태의 정규 파일이 존재할 수 있다.
3. Ext2는 메모리에 거대한 아이노드 변환 테이블을 유지해야만 아이노드 번호를 디스크 주소로 바꿀 수 있다.
확장 속성(Extended Attributes)과 ACL: 아이노드 공간의 한계를 넘다XATTR & ACCESS CONTROL
고작 128바이트에 불과한 아이노드의 공간은 시간이 지남에 따라 점차 부족해졌습니다. 파일시스템에 더 복잡한 보안 컨텍스트나 메타데이터를 저장해야 할 요구가 늘어났기 때문입니다. 이를 해결하기 위해 Ext2는 확장 속성(Extended Attribute)이라는 개념을 도입했습니다. 넘치는 데이터를 아이노드 내부에 억지로 욱여넣는 대신 별도의 디스크 블록에 속성들을 저장하고, 아이노드의 i_file_acl 필드가 해당 블록을 가리키도록 구조를 확장한 것입니다.
확장 속성 블록의 내부 구조는 마치 '사전(Dictionary)'과 같습니다. 블록의 앞쪽에는 속성의 이름(Key)을 담은 엔트리가 쌓이고, 뒤쪽 공간부터는 실제 값(Value)이 채워지며 서로를 참조합니다. 흥미로운 점은, 여러 파일이 완전히 동일한 확장 속성 세트를 갖는다면 중복 저장 없이 하나의 속성 블록을 공유할 수 있어 디스크 낭비를 크게 줄일 수 있다는 점입니다.
이 확장 속성 인프라는 리눅스의 접근 제어 목록(ACL, Access Control List) 기능을 구현하는 데 필수적인 기반이 됩니다. 전통적인 유닉스 권한(rwx) 체계가 소유자, 소유 그룹, 기타 사용자라는 세 가지 범주로만 접근을 통제했다면, ACL은 "특정 사용자 A에게는 읽기 권한만 부여하고, 그룹 B의 접근은 전면 차단한다"와 같은 정밀한 타겟팅 보안을 가능하게 합니다.
- setxattr() / getxattr()
- 파일에 새로운 확장 속성을 부여하거나, 기존에 설정된 속성 값을 읽어옵니다.
- listxattr() / removexattr()
- 파일에 부여된 모든 확장 속성의 목록을 반환하거나, 특정 속성을 영구히 삭제합니다.
- setfacl / getfacl (명령어)
- 사용자 레벨에서 ACL을 조작하는 유틸리티로, 내부적으로 커널의 확장 속성 메커니즘을 호출합니다.
핵심 O/X 퀴즈
1. 확장 속성은 아이노드의 128바이트 크기 제약을 우회하여 추가적인 메타데이터를 저장하기 위한 메커니즘이다.
2. 여러 파일이 동일한 접근 제어 목록(ACL)을 가질 경우, 공간 절약을 위해 단일 확장 속성 블록을 여러 아이노드가 공유할 수 있다.
3. 확장 속성은 오직 시스템 보안 강화를 위한 용도로만 제한되어 있어 일반 사용자는 임의의 속성을 추가할 수 없다.
파일 타입별 블록 사용 전략: 모든 파일이 데이터 블록을 필요로 하는 것은 아니다FILE TYPE DIFFERENCES
일반적으로 파일은 곧 데이터의 집합으로 여겨지지만, 커널 내부에서 다루는 파일(디렉터리, 특수 장치 파일, 파이프, 소켓 등)의 성격은 매우 다양합니다. 시스템 리소스 관점에서 중요한 명제는 "모든 파일 타입이 디스크의 데이터 블록을 소비하지는 않는다"는 사실입니다.
- 빈 정규 파일 (Empty file)
- 내용이 전혀 없는 파일은 메타데이터(아이노드)만 생성될 뿐, 데이터 블록은 단 하나도 할당되지 않습니다.
- 디렉터리 (Directory)
- 디렉터리 역시 일종의 파일이며, 할당된 데이터 블록 내부에 자식 파일들의 이름과 해당 아이노드 번호를 매핑한 엔트리 구조체(`ext2_dir_entry_2`) 목록을 저장합니다.
- 짧은 심볼릭 링크
- 대상 경로가 60자 이하인 경우, 아예 데이터 블록을 거치지 않고 아이노드 본체의 `i_block` 배열 메모리 공간을 직접 문자열 저장소로 전용합니다.
- 특수 파일 (Device, Pipe, Socket)
- 문자/블록 장치 파일이나 파이프 등은 디스크에 데이터를 기록하지 않으므로 데이터 블록이 불필요합니다. 장치의 주 번호(Major)와 부 번호(Minor) 같은 핵심 정보는 아이노드 내부에 안전하게 캡슐화됩니다.
디렉터리에서 특정 파일을 지울 때, Ext2는 남은 파일들의 목록을 앞으로 끌어당겨 정렬하는 수고로운 작업을 하지 않습니다. 대신 삭제된 엔트리의 아이노드 번호를 0으로 마킹하여 '무효화'하고, 바로 직전에 위치한 유효 엔트리의 레코드 길이(rec_len) 속성을 대폭 늘려버립니다. 마치 불량 구역을 우회하는 징검다리를 놓듯, 다음 유효한 엔트리로 단번에 건너뛸 수 있도록 포인터를 덮어쓰는 매우 영리한 방식입니다.
핵심 O/X 퀴즈
1. 방금 `touch` 명령어로 생성된 0바이트 크기의 정규 파일은 적어도 하나의 4KB 데이터 블록을 기본적으로 점유한다.
2. Ext2 디렉터리의 본질은 '파일 이름'과 '아이노드 번호'를 짝지어 놓은 테이블을 담고 있는 특수한 형태의 파일이다.
3. 디렉터리 내에서 파일이 삭제되면, 해당 영역의 블록을 즉시 반환하기 위해 모든 디렉터리 엔트리를 촘촘하게 재배열(Defragmentation)한다.
메모리 속의 Ext2: 디스크 I/O를 최소화하는 캐싱 전략IN-MEMORY CACHING
파일시스템의 메타데이터를 참조할 때마다 기계적으로 구동되는 물리 디스크를 읽어야 한다면 시스템은 극도로 느려질 것입니다. 병목 현상을 타파하기 위해, 시스템은 마운트 시점부터 방대한 양의 디스크 메타데이터를 고속 메모리(RAM) 영역으로 끌어올립니다.
- 영구 상주 (Always Cached)
- 슈퍼블록과 그룹 디스크립터 영역입니다. 파일시스템이 언마운트되기 전까지 절대로 메모리에서 삭제되지 않는, 가장 핵심적인 기반 정보입니다.
- 동적 캐싱 (Dynamic Cached)
- 아이노드, 개별 데이터 블록, 블록 비트맵, 아이노드 비트맵 영역입니다. 필요할 때만 메모리에 로드되며, 시스템 메모리가 부족해지면 커널의 페이지 회수(Page reclaim) 알고리즘에 의해 언제든 디스크로 내려갈 수 있습니다.
- 캐싱 금지 (Never Cached)
- 할당되지 않은 유휴 블록 등 데이터 자체가 무의미한 영역은 굳이 캐시에 올려 메모리를 낭비하지 않습니다.
커널 메모리 구조에서 슈퍼블록은 ext2_sb_info 구조체로 표현됩니다. VFS(가상 파일시스템) 계층이 관리하는 일반적인 super_block 객체 내부에는 개별 파일시스템의 고유 정보를 가리키는 s_fs_info 포인터가 존재하는데, Ext2 환경에서는 이 포인터가 바로 ext2_sb_info와 직접 연결됩니다. 이 거대한 구조체는 디스크 상의 슈퍼블록 필드들은 물론, 이를 담고 있는 메모리 버퍼 헤드(s_sbh)와 수백 개의 그룹 디스크립터 배열 정보까지 모두 통괄합니다.
파일시스템 마운트를 관장하는 ext2_fill_super() 함수는 호출 즉시 ext2_sb_info 객체를 메모리에 생성하고, 디스크로부터 실제 슈퍼블록 데이터와 그룹 디스크립터들을 읽어 들여 버퍼에 적재합니다. 이 복잡한 초기화 과정이 완벽히 마무리되어야만 비로소 VFS가 루트 디렉터리를 기점으로 경로 탐색(Pathname lookup)을 정상 수행할 수 있습니다.
핵심 O/X 퀴즈
1. 시스템 안정성을 위해 Ext2의 슈퍼블록과 그룹 디스크립터 정보는 마운트된 순간부터 항상 메모리에 유지된다.
2. 공간 관리를 담당하는 아이노드 비트맵은 한 번 메모리에 적재되면 커널에 의해 절대 회수(Reclaim)되지 않는다.
3. `ext2_sb_info` 구조체는 추상화된 VFS 객체와 Ext2만의 구체적인 파일시스템 구현 데이터를 연결하는 핵심 교량 역할을 한다.
Ext2 아이노드의 메모리 표현 구조와 mke2fs의 역할INODE IN MEMORY & MKFS
프로세스가 파일에 접근하여 VFS가 특정 디스크 아이노드를 메모리로 호출하면, 커널은 이 정보를 바탕으로 ext2_inode_info라는 통합 객체를 동적으로 생성합니다. 이 거대한 캡슐 안에는 표준적인 vfs_inode 객체뿐만 아니라, 오직 Ext2 구동에만 필요한 심화 메타데이터들이 한데 어우러져 있습니다.
- i_block_group
- 현재 아이노드가 물리적으로 속해 있는 호스트 블록 그룹 번호입니다.
- i_next_alloc_block / goal
- 과거 블록 할당 이력을 바탕으로, 향후 데이터가 추가될 때 가장 파편화가 적을 것으로 예상되는 최적의 다음 블록 주소를 추적합니다.
- i_prealloc_block
- 파일 크기 증가를 대비해 선제적으로 예약해 둔 블록들의 위치와 잔여 수량 정보를 유지합니다.
- xattr_sem
- 여러 스레드가 동시에 확장 속성(Extended Attribute)을 읽고 쓰는 것을 제어하는 동기화 락(Read/Write Semaphore)입니다.
초기 파티션을 포맷하는 mke2fs 유틸리티의 동작 과정도 흥미롭습니다. 이 프로그램은 파티션 공간을 분석하여 블록 크기와 아이노드 비율을 최적화한 뒤, 텅 빈 디스크 위에 슈퍼블록과 디스크립터의 뼈대를 세웁니다. 모든 비트맵을 0으로 초기화하고 깨끗한 아이노드 테이블을 구축한 뒤, 최종적으로 최상위 루트(/) 디렉터리와 시스템 복구용 lost+found 디렉터리를 생성하며 작업을 마무리합니다.
lost+found 디렉터리는 치명적인 디스크 오류 발생 시, 본래 위치(경로)를 상실한 떠돌이 파일 조각들을 e2fsck 프로그램이 임시로 모아두는 일종의 '유실물 센터'입니다. 아무리 용량이 작은 USB 드라이브를 포맷하더라도, 이 디렉터리를 포함한 Ext2의 필수 뼈대는 온전히 갖춰져야만 합니다.
핵심 O/X 퀴즈
1. 커널 메모리 상의 `ext2_inode_info` 구조체 내부에는 VFS 계층에서 통용되는 공통 아이노드 객체 정보도 포함되어 있다.
2. 디스크를 새롭게 포맷하는 `mke2fs` 유틸리티는 기본적으로 루트 디렉터리와 유실물 복구를 위한 `lost+found` 디렉터리를 생성한다.
3. `ext2_inode_info`의 `xattr_sem` 필드는 파일의 일반 데이터 블록에 대한 동시 다발적인 읽기/쓰기를 차단하는 범용 동기화 객체이다.
Ext2 메서드: VFS의 요청을 Ext2만의 방식으로 처리하다VFS METHOD TABLES
가상 파일시스템(VFS)은 커널 내부에서 "아이노드를 읽어라", "파일에 데이터를 써라", "디렉터리에서 특정 이름의 파일을 찾아라" 같은 추상적이고 보편적인 지시를 내립니다. 디스크 레이아웃이 각기 다른 수많은 파일시스템은 이러한 인터페이스를 수용하기 위해 저마다 특화된 세부 함수표(Method Table)를 VFS에 등록해야 합니다.
- alloc_inode / destroy_inode
- 커널의 슬랩 할당자(Slab Allocator)에 요청하여 `ext2_inode_info` 객체의 메모리를 신속히 할당받거나 해제합니다.
- read_inode / write_inode
- 물리 디스크에서 특정 번호의 아이노드 블록을 읽어오거나, 캐시에서 변경된 내역을 디스크로 동기화(Sync)합니다.
- delete_inode
- 파일 삭제 명령이 떨어지면, 해당 아이노드와 연결된 모든 데이터 블록을 회수합니다.
- write_super
- 메모리 상에서 변경된 통계 정보(유휴 블록 수 등)가 담긴 슈퍼블록을 디스크 파티션에 안전하게 덮어씁니다.
아이노드 오퍼레이션의 경우 타겟 파일의 속성에 따라 연결되는 함수가 극명하게 갈립니다. 디렉터리 타입의 아이노드에는 create, lookup, mkdir, rename 등 트리 계층의 네임스페이스를 관리하는 함수들이 매핑됩니다. 반면 정규 파일에는 truncate, 파일 권한 갱신, 확장 속성 관련 함수들이 매핑되어 데이터 자체를 제어합니다.
한 가지 놀라운 사실은, Ext2가 파일 I/O 시스템 콜을 처리할 때 밑바닥부터 모든 코드를 새로 짜지 않았다는 점입니다. 일반적인 데이터 읽기와 쓰기는 리눅스 커널의 범용 함수인 generic_file_read()와 generic_file_write()에 전적으로 위임합니다. Ext2 기술력의 핵심은 데이터 자체의 복사가 아니라, "새로운 데이터를 기록하기 위해 물리 디스크의 어느 빈 블록을 선택하고, 복잡한 비트맵 체계를 어떻게 무결하게 조작할 것인가"라는 로우레벨 공간 관리 알고리즘에 집중되어 있습니다.
핵심 O/X 퀴즈
1. VFS의 추상적인 명령은 파일시스템별로 사전에 등록된 함수 포인터 테이블을 거쳐 실제 구동 코드로 연결된다.
2. 디렉터리 아이노드 연산에는 일반적인 파일 데이터 자르기(truncate) 함수가 아닌, 이름 공간 탐색(lookup)이나 이름 변경(rename) 같은 구조 관리 함수들이 연결된다.
3. 보안과 최적화를 위해 Ext2는 데이터 읽기 및 쓰기 동작을 오로지 자신만의 독자적인 코드로만 처리하며 공용 커널 함수를 배척한다.
아이노드의 생성과 삭제: 새로운 파일은 어느 블록 그룹에 자리 잡을까?INODE ALLOCATION POLICY
성능이 뛰어난 파일시스템 설계자들은 항상 두 가지 난제와 싸웁니다. 첫째는 사용 시간이 길어질수록 파일이 쪼개지는 파편화(Fragmentation) 현상을 억제하는 것이고, 둘째는 파일의 특정 논리적 오프셋을 던져주었을 때 그 위치에 대응하는 실제 물리 디스크 블록을 최대한 빠르게 찾아내는 것입니다.
새로운 아이노드를 발급하는 핵심 함수인 ext2_new_inode()는 아무 빈자리나 무작정 선택하지 않습니다. 새로 생성될 파일이 디렉터리인지 정규 파일인지 그 성격에 따라 고도의 휴리스틱 전략을 구사합니다.
ext2_free_inode() 함수는 할당의 정확한 역순입니다. 아이노드 비트맵의 플래그를 지우고(Clear), 유휴 공간 카운터를 복구하며, 만약 삭제 대상이 디렉터리였다면 전체 디렉터리 카운터 통계 수치도 함께 삭감합니다.부채(Debt) 메커니즘은 특정 블록 그룹이 디렉터리만 비정상적으로 많이 떠안는 상황을 감지하고 차단하는 일종의 가중치 밸런서입니다. 한 그룹에 디렉터리를 새로 생성하면 부채 스코어가 상승하고, 디렉터리 외의 일반 파일을 생성하면 부채가 차감됩니다. 시스템은 부채 스코어가 너무 높은 그룹에 새로운 디렉터리가 생성되는 것을 회피하여 디스크 전역의 부하 균형을 맞춰냅니다.
핵심 O/X 퀴즈
1. 최적의 디스크 I/O 성능을 위해 디렉터리 생성과 정규 파일 생성 알고리즘은 내부적으로 다른 휴리스틱을 적용받는다.
2. 디스크에서 빈 아이노드 공간을 하나 할당받는 행위의 기술적 본질은 특정 비트맵의 0 값을 1로 반전시키는 것이다.
3. 커널이 아이노드를 성공적으로 삭제하려면, 아이노드 비트맵만 업데이트한 뒤 복잡한 데이터 블록 삭제 작업은 생략해도 무방하다.
데이터 블록 주소 지정(Addressing): 파일의 n번째 블록은 디스크의 어디에 위치할까?Figure 18-5 — i_block ARRAY
일반적인 애플리케이션 프로그램은 파일을 끊김 없이 연결된 1차원 바이트 배열로 취급합니다. 즉 "파일의 처음부터 5,000번째 오프셋에 있는 문자열"과 같은 방식으로 접근합니다. 하지만 실제 물리 디스크 상에서 그 데이터 블록들이 항상 연속적으로 나열되어 있는 것은 아닙니다. 그렇다면 커널은 애플리케이션의 이 논리적인 오프셋 요구를 어떻게 실제 디스크 블록 좌표로 정밀하게 치환해 낼까요?
i_block[0]부터 i_block[11] 배열 안에는 실제 디스크의 물리적 블록 주소가 그대로 저장되어 있습니다. 추가적인 메타데이터 조회 없이 가장 빠른 속도로 데이터에 직행합니다.i_block[13]은 포인터 블록을 가리키는 포인터 블록(2단계 매핑)을, i_block[14]는 3단계의 거대한 탐색 트리를 구성하여 초대형 파일의 구석구석을 커버합니다.- 1KB 블록 시스템
- 다이렉트 영역 12KB, 싱글 인다이렉트 한계치 약 268KB. 가장 깊은 트리플 인다이렉트를 총동원해도 시스템 한계상 단일 파일 최대 크기는 약 16GB에 그칩니다.
- 4KB 블록 시스템
- 다이렉트 영역 48KB, 싱글 인다이렉트로 약 4MB 수용. 더블 인다이렉트로 약 4GB를 돌파하며, 트리플 인다이렉트 구성 시 단일 파일당 약 4TB라는 방대한 용량을 지원할 수 있습니다.
이 복잡한 다단계 트리(Trie) 구조의 핵심 철학은 "대부분의 유닉스 시스템 파일은 크기가 매우 작다"는 통계적 사실에 기반합니다. 파일이 12블록 이하의 소형이라면 번거로운 인다이렉트 참조 단계를 거치지 않고 아이노드 내부 포인터만으로 모든 처리가 끝납니다. 또한 최악의 탐색 시나리오라 하더라도, 커널의 막강한 페이지 캐시 시스템 덕분에 매 단계마다 느린 물리 디스크 I/O가 발생하지는 않습니다.
핵심 O/X 퀴즈
1. 특정 데이터 바이트의 오프셋을 현재 파티션의 블록 크기 단위로 나누면, 해당 데이터가 위치한 파일 내부의 상대적 블록 인덱스를 도출할 수 있다.
2. Ext2 설계상 `i_block` 배열 내의 모든 원소는 일관성을 위해 무조건 트리플 인다이렉트 매핑 구조를 강제한다.
3. 시스템 포맷 시 파티션의 기본 블록 크기를 크게 잡을수록, 하나의 인다이렉트 블록 박스 안에 우겨넣을 수 있는 주소 포인터의 개수도 선형적으로 증가한다.
파일 홀(File Hole)과 데이터 블록 할당: 논리적 크기와 물리적 크기의 차이HOLES & ALLOCATION POLICY
파일 홀(File hole)이란, 애플리케이션이 파일의 특정 위치로 포인터를 강제로 건너뛰어(Seek) 뒷부분에만 데이터를 기록했을 때, 앞부분에 생겨난 거대한 '공백 지대'를 의미합니다. 터미널의 cat 명령어로 읽어보면 이 공백 영역이 전부 널 문자(Null character)의 바다처럼 보이지만, 운영체제는 이 허상의 공백 구간에 귀중한 디스크 물리 블록을 낭비하여 할당하지 않습니다.
만약 6,144개의 널 문자 공간을 빈 채로 남겨둔 채 끝자락에 단 하나의 'X'라는 문자만 기록된 파일(논리적 크기 6,145바이트)을 4KB 블록 기반 Ext2 환경에 저장한다고 가정해 보겠습니다. 이 경우 시스템은 수많은 빈 블록을 잡지 않고, 오직 'X' 문자가 속한 4KB 데이터 블록 단 1개만을 디스크에 할당합니다. 이때 메타데이터 i_size는 애플리케이션의 눈을 속여 6,145를 반환하지만, 실제 점유율을 나타내는 i_blocks는 단지 8섹터(4KB 블록 하나 분량)만을 정직하게 가리키게 됩니다. 비어있는 홀 구간에 해당하는 주소 포인터들은 모두 0 값을 유지합니다.
파일 내용이 추가되어 새로운 물리 데이터 블록을 발급받아야 할 때, ext2_new_block() 커널 함수는 치명적인 디스크 단편화 현상을 억제하기 위해 대단히 치밀한 위치 선정 로직을 거칩니다.
핵심 O/X 퀴즈
1. 파일 내부의 거대한 널(Null) 공백 구간인 '파일 홀'은 논리적 파일 사이즈 통계에는 반영되지만 실제 디스크 섹터를 차지하지는 않을 수 있다.
2. Ext2 커널 모듈은 파일 데이터가 선형적으로 팽창할 때 발생하는 디스크 파편화를 저지하기 위해 주변 블록을 미리 선점해 두는 기능을 가지고 있다.
3. 파일의 메타데이터인 `i_size` 변수와 `i_blocks` 변수는 표현 단위만 다를 뿐, 결과적으로 항상 정확히 일치하는 용량 값을 지시해야 정상이다.
데이터 블록 해제 및 Ext3로의 전환: Ext2에 작업 일지를 더하다FREE & TRANSITION TO EXT3
사용자가 파일을 삭제하거나 길이를 강제로 0으로 잘라내면(Truncate), 시스템은 기존에 점유하던 방대한 데이터 블록 자원을 즉각적으로 환수해야 합니다. 해제 루틴인 ext2_truncate() 함수는 아이노드의 i_block 배열을 꼼꼼히 역추적하며 다이렉트 블록은 물론이고 거미줄처럼 엮인 다중 인다이렉트 포인터 트리 구조를 일망타진합니다. 여기서 명심할 점은 말단에 매달린 실제 데이터 덩어리뿐만 아니라, 주소 좌표만을 보관하고 있던 인다이렉트 중계 블록들조차 용도를 다했다면 한 치의 오차 없이 함께 파기해야 한다는 것입니다.
공간 해제의 최종 물리적 타격은 ext2_free_blocks() 함수가 집행합니다. 할당되어 있던 해당 블록 번호의 비트맵 영역을 0으로 깨끗이 소거(Clear)하고, 지역 그룹 디스크립터 내부의 가용 블록 카운터(bg_free_blocks_count) 수치를 높이며, 글로벌 슈퍼블록의 누적 유휴 블록 수치까지 함께 연동 상승시킵니다. 시스템 메커니즘 관점에서 무언가를 생성한다는 것은 '비트맵의 불을 켜고(1) 잔고 카운터를 깎는 행위'이고, 파괴한다는 것은 '비트맵의 불을 끄고(0) 카운터 잔고를 복원하는 행위'에 불과합니다.
Ext2 시스템이 보여준 안정성과 기계적 완성도에도 불구하고, 대용량 서버 시장에서는 '저널링(Journaling) 부재'라는 태생적 한계가 뼈아프게 작용했습니다. 페타바이트급으로 비대해진 스토리지 환경에서 정전 등으로 시스템이 비정상 셧다운 될 경우, 전체 데이터 영역을 하나하나 훑고 지나가는 무식한 e2fsck 스캔 복구 과정은 수십 시간의 기약 없는 다운타임을 강제했습니다. 리눅스 커널 진영은 이 고질적인 악몽을 극복하고자, Ext2의 안정적인 디스크 레이아웃 유산은 고스란히 계승한 채 오직 '저널'이라는 안전장치만을 덧씌운 걸작, Ext3 파일시스템을 세상에 선보이게 됩니다. 설계의 근간이 완벽히 동일하므로, 안전하게 마운트가 해제된 Ext3 파티션은 아무런 변환 과정 없이 곧바로 구형 Ext2 시스템으로 호환 마운트될 수 있는 기염을 토합니다.
핵심 O/X 퀴즈
1. 데이터 자르기 작업(`ext2_truncate()`) 진행 시, 시스템은 순수 데이터가 담긴 말단 블록들뿐만 아니라 주소를 중계하던 다단계 인다이렉트 메타 블록 구조까지 모두 추적하여 회수망에 포함시켜야 한다.
2. 차세대 파일시스템인 Ext3는 안정성을 담보하기 위해 전작인 Ext2의 디스크 레이아웃 규격을 완전히 파기하고 백지상태에서 새롭게 설계되었다.
3. Ext3 파일시스템의 심화 원리를 파악하기 위해서는, 그 토대가 되는 Ext2 시스템의 아이노드 및 비트맵 배치 구조에 대한 선행 학습 지식이 철저히 배제되어도 무방하다.
저널링(Journaling)의 기본 원리: 전체 스캔 대신 작업 일지만 확인하다JOURNALING FUNDAMENTALS
고전적인 파일시스템이 가진 가장 치명적인 아킬레스건은 어플리케이션이 요구한 수정 사항들이 곧바로 단단한 물리 디스크 원판에 긁혀 기록되지 않는다는 데 있습니다. 최적화를 명목으로 수많은 변경 블록들이 휘발성 램(RAM) 영역에 아슬아슬하게 머물다 한꺼번에 쏟아져 내려가는데(Flush), 이 절체절명의 타이밍에 전원 공급이 끊기면 대참사가 벌어집니다. 어떤 메타데이터 파편은 운 좋게 디스크에 안착했지만, 연결고리가 되는 나머지 파편들은 여전히 메모리 공중에서 증발해버려 전체 파일시스템이 앞뒤가 맞지 않는 '불일치 상태(Inconsistent state)'에 빠져버리는 것입니다.
- 저널 커밋 도장 직전 셧다운 발생
- 격리 장부(저널) 안에 적어 내리던 기록 자체가 불완전하게 끊겨 있습니다. 재부팅 시 fsck 프로세스는 이 찢겨진 미완성 기록을 단호하게 무시하고 파기해 버립니다. 비록 사용자가 작성 중이던 최신 데이터 조각은 허무하게 날아갔을지언정, 파일시스템 전체의 뼈대와 일관성만큼은 완벽하게 무결한 상태를 유지합니다.
- 저널 커밋 도장 직후 셧다운 발생
- 격리 장부에 남은 기록은 완벽히 유효하지만, 이를 본판 파일시스템 자리에 옮겨 적는 도중에 사고가 터졌습니다. 재부팅 시 fsck 프로세스는 온전한 장부의 내용을 바탕으로 본판 파일시스템에 해당 기록을 맹목적으로 다시 덮어씌워(Replay), 멈춰버린 일관성의 시계를 강제로 정상화시킵니다.
저널링 시스템을 만능의 마법으로 과대포장해서는 안 됩니다. 이 기술이 보장하는 한계선은 어디까지나 커널 내부의 로우레벨 시스템 콜(System call) 단위의 원자성과 무결성에 머무릅니다. 예컨대 10GB짜리 거대한 동영상 파일을 복사하는 무거운 작업은 커널 내부적으로 수천 개의 자잘한 write() 시스템 콜 묶음으로 쪼개져 처리됩니다. 만약 50% 복사 시점에 코드가 뽑힌다면, 저널링은 파일시스템 트리가 망가지는 것을 철통같이 막아줄 뿐, 반쪽짜리 영상 파일이 멀쩡한 상태로 부활하도록 보장해 주는 기적이 아닙니다. 애플리케이션 차원의 의미론적 트랜잭션 성공 여부는 별개의 영역입니다.
핵심 O/X 퀴즈
1. 저널링 아키텍처의 도입 목적은, 비정상 셧다운 발생 이후 수억 개의 메타데이터 전체를 맹목적으로 전수 검사해야만 했던 지옥 같은 다운타임을 획기적으로 압축하는 데 있다.
2. 커밋 도장이 채 찍히기도 전에 전원이 차단되어 불완전하게 잘려나간 저널 레코드는, 복구 에이전트에 의해 어떠한 신뢰도 얻지 못하고 가차 없이 폐기된다.
3. 파일시스템에 저널링 기능이 활성화되어 있다면, 데이터베이스처럼 여러 시스템 콜로 구성된 방대한 애플리케이션 트랜잭션 로직 전체가 100% 무결하게 완료될 것을 시스템 레벨에서 강력히 보장받는다.
Ext3의 세 가지 저널링 모드: 안전성과 성능의 트레이드오프JOURNAL / ORDERED / WRITEBACK
Ext3 시스템 설계자들은 모든 서버 관리자의 요구사항이 획일적이지 않다는 점을 이해했습니다. 그래서 무엇을 어디까지 장부(Journal)에 꼼꼼히 기록할 것인지 그 수위를 관리자가 직접 선택할 수 있는 세 가지 파생 모드를 제공합니다. 이 선택에 따라 디스크 I/O 성능과 최악의 사고 시나리오에서의 데이터 보존 한계선이 극명하게 갈립니다.
메타데이터 블록 조작뿐만 아니라, 거대한 파일 데이터 원본 덩어리 전체의 변경 내역마저 모조리 일지 장부에 사본으로 때려 넣습니다. 가장 완벽한 데이터 안전망을 자랑하지만, 모든 거대 데이터를 장부와 본판에 중복으로 두 번씩 써 내려가야 하므로 시스템 속도는 처참하게 추락합니다.
일지 장부에는 뼈대인 '메타데이터'의 조작 내역만 슬림하게 적어 내립니다. 단, 메타데이터 장부 커밋 도장이 찍히기 직전에, 그와 연관된 막대한 실제 데이터 덩어리들이 반드시 본판 디스크로 먼저 쏟아져 내려가도록 I/O 순서(Ordering)의 벽을 굳건히 세워 보장합니다. 극강의 성능과 든든한 안정성 사이의 황금 밸런스를 이룩한 모델입니다.
장부에는 오로지 메타데이터만 기입하며, 실제 데이터 블록이 언제 본판 디스크로 쏟아질지 그 어떠한 순차적 연관성도 강제하지 않습니다. 디스크 I/O 최적화 측면에서는 압도적인 퍼포먼스를 뽐내지만, 셧다운 직후 복구를 마쳤을 때 엉뚱한 이전 데이터 찌꺼기들이 파일 내부에 튀어나올 위험성이 상존합니다.
실제 글로벌 운영 환경에서 대다수의 리눅스 배포판이 ordered 모드를 표준 디폴트 값으로 채택한 데에는 명확한 이유가 있습니다. 모든 영상, 로그 원본을 장부에 욱여넣는(journal 모드) 짓은 디스크 대역폭의 숨통을 조이는 자해 행위이고, 속도에만 눈이 멀어 순서를 무시하는(writeback 모드) 것은 엔터프라이즈의 무결성을 도박판에 올리는 것과 다름없습니다. ordered 모드는 뼈대(메타데이터)만 가볍게 저널링하는 효율을 취하면서도, 실 데이터 살코기가 먼저 디스크 본판에 도착하게끔 순서를 통제하여 '알맹이 없는 껍데기만 남는' 끔찍한 사태를 교묘하게 차단한 최적의 타협안입니다.
핵심 O/X 퀴즈
1. 가장 가혹한 세팅인 `journal` 모드를 적용하면, 파일 권한 같은 뼈대 정보뿐만 아니라 동영상 파일의 거대한 바이트 스트림 원본까지 모조리 저널 영역에 중복 기록된다.
2. 표준 설정인 `ordered` 모드는 저널 장부에 메타데이터만 콤팩트하게 남기면서도, 연관된 실제 데이터 덩어리가 장부 커밋보다 무조건 디스크에 먼저 내려앉도록 순차 락(Ordering lock)을 강제한다.
3. `writeback` 모드는 세 가지 선택지 중 보안성 및 데이터 무결성 보존 측면에서 단연코 가장 우월하고 안전한 최상위 모드로 칭송받는다.
JBD 계층: Ext3의 저널을 전담하는 관리 레이어LOG RECORD · HANDLE · TRANSACTION
고도화된 모듈화 철학을 따르는 리눅스 커널에서, Ext3 파일시스템 모듈 하나가 무거운 저널 장부의 관리 스택 전체를 혼자 짊어지지는 않습니다. 대신 이 무거운 짐을 범용 커널 레이어인 JBD (Journaling Block Device) 서브시스템으로 우아하게 떠넘깁니다. Ext3의 눈에 보이지 않는 저널 데이터베이스는 일반 파일시스템 루트 최상단에 은밀하게 몸을 숨긴 .journal이라는 이름의 특수 파일 형태로 디스크 공간을 점유하고 있습니다.
- 로그 레코드 (Log Record)
- 저널링 시스템이 포착한 디스크 단일 블록에 대한 최소 업데이트 파편입니다. JBD 설계 사상에서 흥미로운 점은, 수정된 1바이트 핀포인트 오프셋을 쪼잔하게 추적하는 대신, 수정이 발생한 블록 자체의 메모리 버퍼 통째를 하나의 거대한 레코드로 통쾌하게 다룬다는 점입니다. 꼬리표(Tag)에는 파일시스템 상의 진짜 타겟 논리 블록 좌표와 플래그 상태가 각인됩니다.
- 원자적 연산 핸들 (Atomic Operation Handle)
- 파일 크기 확장 등과 같은 고차원적인 변경 작업을 논리적으로 하나로 묶어버리는 접착제 단위입니다.
handle_t구조체로 추상화되며, 최악의 사고가 터지더라도 이 핸들에 묶인 동작들은 '전부 다 실행되거나, 아예 1비트도 손대지 않거나(All or Nothing)'의 운명 공동체로 영속성을 보장받습니다. 현재 내달리고 있는 활성 핸들의 포인터 주소는 시스템 프로세스 제어 블록(task_struct)의journal_info영역에 깊숙이 꽂혀 있습니다. - 트랜잭션 (Transaction)
- 수백, 수천 개의 원자적 핸들 묶음들을 다시 거대한 하나의 포장지로 싸버리는 묵직한 최종 덩어리 단위입니다. 물리적 저널 디스크의 촘촘히 연속된 섹터 궤도에 융단 폭격하듯 연속 저장됩니다. 시스템 부팅 시 무결성 검증 단계에서 오직 완전히 포장이 끝난(Complete) 트랜잭션 덩어리만을 신뢰할 수 있는 구세주로 간주하여 디스크 본판에 리플레이(Replay)를 집행합니다.
- T_RUNNING
- 펄펄 끓는 용광로 상태. 파일시스템의 새로운 핸들 덩어리들을 탐욕스럽게 집어삼키는 활성 트랜잭션 모드입니다.
- T_LOCKED
- 용광로의 셔터를 내린 상태. 외부에서 날아오는 신규 핸들의 유입은 단호히 거부하지만, 아직 내부에서 소화가 덜 끝난 기존 핸들 연산들이 잔존해 있습니다.
- T_FLUSH
- 내부 소화가 끝나고 드디어 용암을 쏟아내는 상태. 모든 연산이 종료되었고 방대한 로그 레코드 조각들이 물리 저널 디스크 영역을 향해 격렬하게 하강 중인 상황입니다.
- T_COMMIT
- 도장을 찍기 일보 직전 상태. 레코드들의 물리 디스크 전송은 끝났으나, JBD 관리자로부터 "정상 완료" 도장을 받기 위해 펜을 들고 있는 아슬아슬한 시점입니다.
- T_FINISHED
- 영속성이 부여된 불멸의 상태. 완벽하게 밀봉된 트랜잭션이며, 셧다운 시에도 e2fsck 툴이 전폭적인 신뢰를 보내며 파일시스템 복구의 밑거름으로 기꺼이 활용합니다.
가장 로우레벨 관점에서 JBD 엔진이 메모리의 늪에서 특정 버퍼를 장악하게 되면, 그 즉시 일반 buffer_head 객체 옆구리에 journal_head라는 이질적인 촉수를 꽂아버리고 BH_JBD라는 공포스러운 플래그 낙인을 찍어버립니다. 이것은 리눅스 커널 전체를 향해 "이 버퍼는 동네북처럼 굴러다니는 평범한 Dirty 버퍼가 아니니 함부로 디스크에 내리지 마라. 오직 엄격한 저널 프로토콜의 통제 하에서만 다루어져야 한다"라고 선전포고를 날리는 섬뜩한 표식입니다.
핵심 O/X 퀴즈
1. JBD 서브시스템은 오직 Ext3 파일시스템만을 위해 종속적으로 하드코딩된, Ext3 모듈 내부 깊숙한 곳에 얽혀있는 로컬 커널 드라이버 계층이다.
2. 원자적 연산 핸들(Atomic operation handle) 메커니즘은 고차원적인 파일시스템 조작 하나를 수많은 로우레벨 디스크 블록 업데이트와 한 묶음으로 결속시키는 강력한 족쇄 역할을 수행한다.
3. 재부팅 복구 단계에 돌입하면, fsck 프로세스는 완성되지 못하고 잘려 나간 T_COMMIT 직전의 찌그러진 트랜잭션 기록마저도 긁어모아 맹목적으로 디스크 리플레이 연산에 투입시켜야만 한다.
Ext3의 쓰기 파이프라인: write() 호출이 저널을 통과하기까지END-TO-END WRITE PATH
사용자 영역(User-space) 애플리케이션에서 Ext3 디스크 위에 둥지를 튼 평범한 텍스트 파일에 write() 시스템 콜 탄환을 발사했다고 칩시다. 이 한 줌의 데이터 쪼가리가 도대체 어떤 험난한 미로를 뚫고 JBD 엔진의 검문소를 통과하여 최종 물리 디스크 원판에 아로새겨지는 것일까요?
generic_file_write() 함수 스트림에 조용히 몸을 싣습니다.journal_start() 엔진을 킥오프하여 거대한 원자적 작전(Atomic operation)의 개시를 선언하고, 현재 JBD 용광로(Active transaction) 안으로 새로운 핸들 바구니를 밀어 넣는 것입니다.block_prepare_write()의 은밀한 지시에 따라 ext3_get_block() 측량사가 파일 안의 논리 좌표를 물리 디스크 절대 좌표로 정밀 환산합니다. 만일 텅 빈 공백 구간이었다면 무자비하게 비트맵을 갱신하며 신규 물리 블록을 강제 징발해 냅니다.journal_dirty_metadata() 함수가 버퍼 이마에 낙인을 찍어 JBD 감시망의 특별 더티(Dirty) 큐로 집어 던집니다.journal 모드는 실 데이터 버퍼조차 트랜잭션 리스트에 포박해 버립니다. 표준인 ordered 모드는 데이터를 트랜잭션 리스트에 살짝 걸쳐두기만 하되, 향후 메타데이터 커밋 도장이 찍히기 직전에 실 데이터가 디스크 본판으로 다이빙하도록 강제적인 대기열(Ordering)을 짭니다. 방임주의인 writeback 모드는 실 데이터를 일반 커널 더티 큐에 휙 던져버린 채 순서 보장을 내팽개칩니다.write() 시스템 콜의 사용자 여정은 journal_stop()을 기점으로 겉보기엔 우아하게 끝이 납니다. 하지만 지하 세계에서 JBD 엔진은 여전히 울부짖고 있습니다. 만일 ordered 모드 상태였다면 실 데이터 버퍼들의 물리 I/O 플러시(Flush) 폭우를 먼저 쏟아부은 뒤, 마침내 메타데이터들을 저널 디스크 위에 묵직하게 새겨 넣습니다. 훗날 체크포인트(Checkpoint) 스위퍼가 순찰을 돌며 이 저널 기록들을 본판 파일시스템으로 무사히 덮어씌웠음이 확인되면, 그제야 길었던 트랜잭션 장부 영역의 철거 명령(재사용)이 하달됩니다.고도화된 JBD 모터펌프의 가장 아찔한 기믹 중 하나인 journal_get_write_access() 함수의 진가가 여기서 드러납니다. 타겟 메타데이터 버퍼가 "이미 지나간 과거의 트랜잭션 커밋 큐에 물려 아직 디스크로 내려가지 못한 찰나의 상태"일 경우, 신규 데이터로 메모리를 다이렉트로 덮어버리면 과거 트랜잭션 장부가 영구적으로 오염되는 참사가 벌어집니다. 이를 막기 위해 JBD는 전광석화처럼 해당 메모리 버퍼를 통째로 복제(Clone)해 버려, 과거 트랜잭션은 자신이 알던 그 '옛날 메모리'를 품고 무사히 장부 커밋을 마무리 짓도록 평행우주를 분기시켜 버립니다.