파일 시스템 구현 ⭐⭐⭐⭐
파일 시스템 구현 ⭐⭐⭐⭐¶
개요¶
파일 시스템이 디스크에 데이터를 어떻게 저장하고 관리하는지 학습합니다. 블록 할당 방식, inode 구조, 저널링, RAID 등 실제 구현에 필요한 핵심 개념을 다룹니다.
목차¶
1. 파일 시스템 구조¶
1.1 디스크 레이아웃¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 일반적인 파일 시스템 레이아웃 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 디스크 전체 │ │
│ ├──────┬──────┬──────┬──────┬──────┬───────────────────────────────┤ │
│ │ MBR │ 파티션│ 파티션│ 파티션│ │ │ │
│ │ │ 1 │ 2 │ 3 │ ... │ 빈 공간 │ │
│ └──────┴──────┴──────┴──────┴──────┴───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 파티션 구조 (ext4 예) │ │
│ ├──────┬──────────┬──────────┬────────────────────────────────────┤ │
│ │ 부트 │ 슈퍼 │ 그룹 │ 블록 그룹들 │ │
│ │ 블록 │ 블록 │ 디스크립터│ 0 │ 1 │ 2 │ ... │ │
│ └──────┴──────────┴──────────┴────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 블록 그룹 구조 │ │
│ ├──────────┬──────────┬──────────┬──────────────────────────────────┤ │
│ │ 블록 │ inode │ inode │ 데이터 블록 │ │
│ │ 비트맵 │ 비트맵 │ 테이블 │ │ │
│ └──────────┴──────────┴──────────┴──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
1.2 주요 구성 요소¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 파일 시스템 구성 요소 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┬────────────────────────────────────────────────┐ │
│ │ 구성 요소 │ 역할 │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ 부트 블록 │ 부트스트랩 코드 (OS 로딩) │ │
│ │ (Boot Block) │ 파티션 0번 블록 │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ 슈퍼블록 │ 파일 시스템 메타데이터 │ │
│ │ (Superblock) │ - 블록 크기, 총 블록/inode 수 │ │
│ │ │ - 자유 블록/inode 수 │ │
│ │ │ - 마운트 횟수, 마지막 검사 시간 │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ 블록 비트맵 │ 각 블록의 사용 여부 (0/1) │ │
│ │ (Block Bitmap) │ 빠른 자유 블록 탐색 │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ inode 비트맵 │ 각 inode의 사용 여부 (0/1) │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ inode 테이블 │ 모든 inode 저장 │ │
│ │ │ 파일 메타데이터의 핵심 │ │
│ ├────────────────┼────────────────────────────────────────────────┤ │
│ │ 데이터 블록 │ 실제 파일/디렉토리 내용 │ │
│ └────────────────┴────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2. 디스크 블록 할당¶
2.1 연속 할당 (Contiguous Allocation)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 연속 할당 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 디렉토리 엔트리: │
│ ┌───────────┬─────────┬────────┐ │
│ │ 파일명 │ 시작블록 │ 길이 │ │
│ ├───────────┼─────────┼────────┤ │
│ │ file_a │ 0 │ 3 │ │
│ │ file_b │ 6 │ 2 │ │
│ │ file_c │ 10 │ 4 │ │
│ └───────────┴─────────┴────────┘ │
│ │
│ 디스크 블록: │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ A │ A │ A │ │ │ │ B │ B │ │ │ C │ C │ C │ C │ │ │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 │
│ │
│ 장점: │
│ - 순차 접근 빠름 (디스크 헤드 이동 최소) │
│ - 직접 접근 쉬움: 블록 n = 시작 + n │
│ │
│ 단점: │
│ - 외부 단편화 발생 (블록 3-5, 8-9 낭비) │
│ - 파일 크기 증가 어려움 │
│ - 미리 크기 알아야 함 │
│ │
│ 사용: CD-ROM, DVD (읽기 전용) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.2 연결 할당 (Linked Allocation)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 연결 할당 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 디렉토리 엔트리: │
│ ┌───────────┬─────────┬─────────┐ │
│ │ 파일명 │ 시작블록 │ 끝블록 │ │
│ ├───────────┼─────────┼─────────┤ │
│ │ file_a │ 0 │ 12 │ │
│ └───────────┴─────────┴─────────┘ │
│ │
│ 디스크 블록 (각 블록에 다음 블록 포인터 포함): │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ 블록 0 │ │ 블록 5 │ │ 블록 8 │ │ 블록 12 │ │
│ │ 데이터 │──▶│ 데이터 │──▶│ 데이터 │──▶│ 데이터 │ │
│ │ 다음: 5 │ │ 다음: 8 │ │ 다음: 12 │ │ 다음: -1 │ │
│ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │
│ 0 5 8 12 │
│ │
│ 장점: │
│ - 외부 단편화 없음 │
│ - 파일 크기 동적 증가 가능 │
│ │
│ 단점: │
│ - 직접 접근 비효율 (n번째 블록 접근 시 n번 따라가야 함) │
│ - 포인터 공간 낭비 (각 블록에 4바이트) │
│ - 신뢰성 문제 (포인터 손상 시 파일 손실) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.3 FAT (File Allocation Table)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ FAT 구조 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ FAT (메모리에 캐시됨) 디스크 블록 │
│ ┌─────────────────┐ ┌───────────────────────────────────┐ │
│ │인덱스│ 다음블록 │ │ │ │
│ ├─────┼──────────┤ │ 0 │ 1 │ 2 │ 3 │ 4 │ ... │ │
│ │ 0 │ 5 │────────────┼─ A ─┼─────┼─ B ─┼─────┼─────┼─────┤ │
│ │ 1 │ FREE │ │ │ │ │ │ │ │ │
│ │ 2 │ 4 │────────────┼─────┼─────┼─────┼─────┼─ B ─┼─────┤ │
│ │ 3 │ FREE │ │ │ │
│ │ 4 │ EOF │────────────┼─────┼─ A ─┼─────┼─────┼─────┼─────┤ │
│ │ 5 │ 7 │ │ │ │
│ │ 6 │ FREE │ │ │ │
│ │ 7 │ EOF │ └───────────────────────────────────┘ │
│ └─────┴──────────┘ │
│ │
│ file_a: 0 → 5 → 7 → EOF │
│ file_b: 2 → 4 → EOF │
│ │
│ 장점: │
│ - FAT가 메모리에 있으면 직접 접근 빠름 │
│ - 포인터가 데이터 블록 외부에 있어 신뢰성 향상 │
│ │
│ 단점: │
│ - FAT 크기가 클 수 있음 (대용량 디스크) │
│ - FAT 손상 시 전체 파일 시스템 문제 │
│ │
│ 사용: MS-DOS, Windows (FAT32), USB 드라이브 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
2.4 인덱스 할당 (Indexed Allocation)¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 인덱스 할당 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 디렉토리 엔트리: │
│ ┌───────────┬───────────┐ │
│ │ 파일명 │ 인덱스블록 │ │
│ ├───────────┼───────────┤ │
│ │ file_a │ 8 │ │
│ └───────────┴───────────┘ │
│ │ │
│ ▼ │
│ 인덱스 블록 (블록 8): 데이터 블록: │
│ ┌───────────────────┐ ┌───────────────────────────────────┐ │
│ │ [0] → 블록 3 │ │ │ │
│ │ [1] → 블록 10 │ │ 3 │ 10 │ 15 │ 21 │ ... │ │
│ │ [2] → 블록 15 │ │데이터│데이터│데이터│데이터│ │ │
│ │ [3] → 블록 21 │ │ │ │ │ │ │ │
│ │ [4] → -1 (끝) │ └───────────────────────────────────┘ │
│ │ ... │ │
│ └───────────────────┘ │
│ │
│ 장점: │
│ - 직접 접근 가능: 블록 n = 인덱스[n] │
│ - 외부 단편화 없음 │
│ │
│ 단점: │
│ - 인덱스 블록 오버헤드 │
│ - 작은 파일에 비효율적 │
│ │
│ 확장: │
│ - 연결 인덱스: 인덱스 블록들을 연결 │
│ - 다단계 인덱스: 트리 구조 (Unix inode) │
│ - 결합 방식: 직접 + 간접 포인터 (Unix inode) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3. inode 구조¶
3.1 Unix/Linux inode¶
┌─────────────────────────────────────────────────────────────────────────┐
│ inode 구조 (Unix/ext4) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ inode │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ mode (권한, 유형) │ │
│ │ uid (소유자) │ │
│ │ gid (그룹) │ │
│ │ size (파일 크기) │ │
│ │ atime (접근 시간) │ │
│ │ mtime (수정 시간) │ │
│ │ ctime (상태 변경 시간) │ │
│ │ link count (하드 링크 수) │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ 블록 포인터 (데이터 위치) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ 직접 포인터 [0-11] → 데이터 블록 (12개) │ │ │
│ │ ├─────────────────────────────────────────────────────────┤ │ │
│ │ │ 단일 간접 [12] → 인덱스 블록 → 데이터 │ │ │
│ │ ├─────────────────────────────────────────────────────────┤ │ │
│ │ │ 이중 간접 [13] → 인덱스 → 인덱스 → 데이터 │ │ │
│ │ ├─────────────────────────────────────────────────────────┤ │ │
│ │ │ 삼중 간접 [14] → 인덱스 → 인덱스 → 인덱스 → 데이터│ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.2 직접/간접 블록 포인터¶
┌─────────────────────────────────────────────────────────────────────────┐
│ inode 블록 포인터 구조 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 블록 크기 = 4KB, 포인터 크기 = 4바이트 │
│ 포인터/블록 = 4KB / 4B = 1024개 │
│ │
│ inode │
│ ┌───────────────┐ │
│ │ 직접 [0] │────────────────────────────────────▶ [데이터 블록] │
│ │ 직접 [1] │────────────────────────────────────▶ [데이터 블록] │
│ │ ... │ │
│ │ 직접 [11] │────────────────────────────────────▶ [데이터 블록] │
│ ├───────────────┤ │
│ │ 단일 간접[12]│──▶┌──────────┐ │
│ │ │ │인덱스블록│──▶ [데이터] × 1024 │
│ │ │ └──────────┘ │
│ ├───────────────┤ │
│ │ 이중 간접[13]│──▶┌──────────┐ ┌──────────┐ │
│ │ │ │인덱스블록│──▶│인덱스블록│──▶ [데이터] × 1024 │
│ │ │ │ × 1024 │ └──────────┘ × 1024 │
│ │ │ └──────────┘ │
│ ├───────────────┤ │
│ │ 삼중 간접[14]│──▶ (3단계 인덱스) │
│ └───────────────┘ │
│ │
│ 최대 파일 크기 계산 (4KB 블록): │
│ 직접: 12 × 4KB = 48KB │
│ 단일 간접: 1024 × 4KB = 4MB │
│ 이중 간접: 1024 × 1024 × 4KB = 4GB │
│ 삼중 간접: 1024 × 1024 × 1024 × 4KB = 4TB │
│ 총: 약 4TB │
│ │
└─────────────────────────────────────────────────────────────────────────┘
3.3 inode 크기 계산 예제¶
// 파일의 n번째 블록 위치 찾기
#define BLOCK_SIZE 4096
#define PTRS_PER_BLOCK (BLOCK_SIZE / sizeof(uint32_t)) // 1024
#define DIRECT_BLOCKS 12
#define SINGLE_INDIRECT_LIMIT (DIRECT_BLOCKS + PTRS_PER_BLOCK)
#define DOUBLE_INDIRECT_LIMIT (SINGLE_INDIRECT_LIMIT + PTRS_PER_BLOCK * PTRS_PER_BLOCK)
uint32_t get_block_number(inode_t* inode, uint32_t logical_block) {
if (logical_block < DIRECT_BLOCKS) {
// 직접 블록
return inode->direct[logical_block];
}
logical_block -= DIRECT_BLOCKS;
if (logical_block < PTRS_PER_BLOCK) {
// 단일 간접
uint32_t* indirect = read_block(inode->single_indirect);
return indirect[logical_block];
}
logical_block -= PTRS_PER_BLOCK;
if (logical_block < PTRS_PER_BLOCK * PTRS_PER_BLOCK) {
// 이중 간접
uint32_t index1 = logical_block / PTRS_PER_BLOCK;
uint32_t index2 = logical_block % PTRS_PER_BLOCK;
uint32_t* level1 = read_block(inode->double_indirect);
uint32_t* level2 = read_block(level1[index1]);
return level2[index2];
}
// 삼중 간접 (생략)
return 0;
}
4. 디렉토리 구현¶
4.1 디렉토리 엔트리 구조¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 디렉토리 구현 방식 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 선형 리스트 (간단한 구현): │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ 디렉토리 파일 내용 │ │
│ ├────────────┬────────────────┬──────────────────────────────────────┤ │
│ │ inode 번호 │ 엔트리 길이 │ 파일 이름 │ │
│ ├────────────┼────────────────┼──────────────────────────────────────┤ │
│ │ 2 │ 12 │ . │ │
│ │ 2 │ 12 │ .. │ │
│ │ 15 │ 16 │ file1.txt │ │
│ │ 23 │ 20 │ documents │ │
│ │ 45 │ 24 │ long_filename.doc │ │
│ └────────────┴────────────────┴──────────────────────────────────────┘ │
│ │
│ ext4 디렉토리 엔트리 (struct ext4_dir_entry_2): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ inode (4바이트) : inode 번호 │ │
│ │ rec_len (2바이트) : 이 엔트리의 총 길이 │ │
│ │ name_len (1바이트) : 파일 이름 길이 │ │
│ │ file_type (1바이트) : 파일 유형 (파일/디렉토리/링크 등) │ │
│ │ name (가변) : 파일 이름 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4.2 해시 테이블 디렉토리¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 해시 테이블 디렉토리 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 파일 이름 → 해시 함수 → 버킷 인덱스 │
│ │
│ 해시 테이블 │
│ ┌─────────┐ │
│ │ 0 │ → [readme.txt, inode 15] │
│ ├─────────┤ │
│ │ 1 │ → [config.json, inode 23] → [data.csv, inode 45] │
│ ├─────────┤ │
│ │ 2 │ → (비어있음) │
│ ├─────────┤ │
│ │ 3 │ → [main.c, inode 67] → [test.c, inode 89] │
│ ├─────────┤ │
│ │ ... │ │
│ └─────────┘ │
│ │
│ 장점: │
│ - 검색 O(1) 평균 (선형 리스트는 O(n)) │
│ - 대용량 디렉토리에서 효과적 │
│ │
│ ext4의 htree (HTree Index): │
│ - B-tree 기반 해시 디렉토리 │
│ - 수천 개 파일도 빠르게 검색 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5. 파일 시스템 예시¶
5.1 FAT32¶
┌─────────────────────────────────────────────────────────────────────────┐
│ FAT32 구조 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Reserved │ FAT │ FAT │ 데이터 영역 │ │
│ │ 영역 │ 1 │ 2 │ (클러스터들) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 특징: │
│ - 클러스터 크기: 4KB ~ 32KB │
│ - FAT 엔트리: 32비트 (28비트 사용) │
│ - 최대 파일 크기: 4GB - 1 (32비트 크기 필드) │
│ - 최대 볼륨 크기: 2TB │
│ │
│ 디렉토리 엔트리 (32바이트): │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 파일명 (8바이트) + 확장자 (3바이트) = 8.3 형식 │ │
│ │ 속성 (1바이트): 읽기전용, 숨김, 시스템, 디렉토리 등 │ │
│ │ 생성/수정/접근 시간 (10바이트) │ │
│ │ 시작 클러스터 번호 (4바이트) │ │
│ │ 파일 크기 (4바이트) │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ VFAT: 긴 파일 이름 지원 (별도 엔트리 사용) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
5.2 ext4¶
┌─────────────────────────────────────────────────────────────────────────┐
│ ext4 주요 특징 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Extents (익스텐트) │
│ 연속된 블록을 하나의 엔트리로 표현 → 대용량 파일에 효율적 │
│ ┌──────────────────────────────────────────────┐ │
│ │ 시작 블록: 1000, 길이: 500 │ │
│ │ → 블록 1000~1499를 한 번에 참조 │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ 2. 저널링 (Journaling) │
│ - 메타데이터 저널: 기본 모드, 빠름 │
│ - 데이터 저널: 데이터까지 저널링, 안전 │
│ │
│ 3. 대용량 지원 │
│ - 최대 파일 크기: 16TB │
│ - 최대 볼륨 크기: 1EB (Exabyte) │
│ │
│ 4. 기타 기능 │
│ - Delayed allocation: 쓰기 최적화 │
│ - Multi-block allocation: 여러 블록 한 번에 할당 │
│ - 디렉토리 htree: 빠른 검색 │
│ - 온라인 조각 모음 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6. 저널링¶
6.1 저널링의 필요성¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 저널링 개념 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 문제: 파일 시스템 비일관성 │
│ │
│ 파일 삭제 과정 (저널링 없이): │
│ 1. 디렉토리에서 엔트리 제거 │
│ 2. inode를 자유 목록에 추가 ← 여기서 크래시! │
│ 3. 데이터 블록을 자유 목록에 추가 │
│ │
│ 크래시 후 문제: │
│ - 디렉토리에서 삭제됨 │
│ - 하지만 inode와 블록은 여전히 '사용 중' 표시 │
│ - 해당 공간 접근 불가 + 복구 불가 │
│ │
│ 해결: 저널링 │
│ 변경 전에 로그(저널)에 기록 → 크래시 후 로그 기반 복구 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6.2 저널링 동작¶
┌─────────────────────────────────────────────────────────────────────────┐
│ 저널링 동작 방식 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 저널 영역 │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ TxB │ 데이터블록 │ 메타데이터 │ TxE │ TxB │ ... │ TxE │ ... │ │
│ └──┬──┴───────────┴────────────┴──┬──┘ │ │
│ │ │ │ │
│ │ Transaction 1 │ Transaction 2 │ │
│ │ │ │ │
│ TxB: Transaction Begin │ │
│ TxE: Transaction End (Commit) │ │
│ │
│ 쓰기 과정: │
│ 1. Transaction Begin 기록 │
│ 2. 변경할 블록들을 저널에 기록 │
│ 3. Transaction End 기록 (커밋) │
│ 4. 실제 위치에 데이터 쓰기 (Checkpoint) │
│ 5. 저널에서 해당 트랜잭션 제거 │
│ │
│ 복구 과정 (부팅 시): │
│ 1. 저널 검사 │
│ 2. 완료된 트랜잭션 (TxE 있음): 실제 위치에 다시 쓰기 │
│ 3. 미완료 트랜잭션 (TxE 없음): 무시 (rollback) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6.3 저널 모드¶
┌─────────────────────────────────────────────────────────────────────────┐
│ ext4 저널 모드 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┬──────────────────────────────────────────────────┐ │
│ │ 모드 │ 설명 │ │
│ ├───────────────┼──────────────────────────────────────────────────┤ │
│ │ journal │ 데이터 + 메타데이터 모두 저널링 │ │
│ │ │ 가장 안전하지만 가장 느림 (2배 쓰기) │ │
│ ├───────────────┼──────────────────────────────────────────────────┤ │
│ │ ordered │ 메타데이터만 저널링 │ │
│ │ (기본값) │ 데이터 먼저 쓰고 메타데이터 저널링 │ │
│ │ │ 데이터 일관성 보장, 적절한 성능 │ │
│ ├───────────────┼──────────────────────────────────────────────────┤ │
│ │ writeback │ 메타데이터만 저널링 │ │
│ │ │ 데이터 쓰기 순서 보장 안 함 │ │
│ │ │ 가장 빠르지만 크래시 시 데이터 손실 가능 │ │
│ └───────────────┴──────────────────────────────────────────────────┘ │
│ │
│ # 마운트 시 옵션 지정 │
│ $ mount -o data=ordered /dev/sda1 /mnt │
│ $ mount -o data=journal /dev/sda1 /mnt │
│ $ mount -o data=writeback /dev/sda1 /mnt │
│ │
└─────────────────────────────────────────────────────────────────────────┘
7. RAID¶
7.1 RAID 개요¶
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 개요 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ RAID = Redundant Array of Independent Disks │
│ │
│ 목표: │
│ 1. 성능 향상 (Striping - 데이터 분산) │
│ 2. 신뢰성 향상 (Mirroring/Parity - 중복 저장) │
│ 3. 대용량 (여러 디스크 결합) │
│ │
│ 구현 방식: │
│ - 하드웨어 RAID: RAID 컨트롤러 카드 │
│ - 소프트웨어 RAID: OS에서 구현 (mdadm) │
│ - 하이브리드: 메인보드 내장 RAID │
│ │
└─────────────────────────────────────────────────────────────────────────┘
7.2 RAID 레벨¶
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 0 (Striping) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 데이터: A B C D E F G H │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 디스크0 │ │ 디스크1 │ │ 디스크2 │ │
│ │ A D G │ │ B E H │ │ C F │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 특징: │
│ - 모든 디스크에 데이터 분산 (스트라이핑) │
│ - 읽기/쓰기 속도: n배 향상 │
│ - 용량: 디스크 수 × 단일 용량 │
│ - 중복 없음: 한 디스크 고장 → 전체 데이터 손실 │
│ - 용도: 성능 중요, 데이터 손실 감수 가능한 경우 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 1 (Mirroring) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ 데이터: A B C D │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 디스크0 │ │ 디스크1 │ │
│ │ A B C D │ ◀── 동일 ──▶ │ A B C D │ │
│ │ (원본) │ │ (미러) │ │
│ └─────────┘ └─────────┘ │
│ │
│ 특징: │
│ - 완벽한 복제 (미러링) │
│ - 읽기 속도: 2배 (두 디스크에서 병렬 읽기) │
│ - 쓰기 속도: 동일 (두 곳에 써야 함) │
│ - 용량: 단일 디스크 용량 (50% 효율) │
│ - 한 디스크 고장해도 데이터 보존 │
│ - 용도: 중요 데이터, 시스템 드라이브 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 5 (Striping with Parity) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
│ │ 디스크 0 │ 디스크 1 │ 디스크 2 │ 디스크 3 │ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ A1 │ A2 │ A3 │ Ap (패리티)│ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ B1 │ B2 │ Bp (패리티)│ B3 │ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ C1 │ Cp (패리티)│ C2 │ C3 │ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ Dp (패리티)│ D1 │ D2 │ D3 │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
│ │
│ 패리티 계산: Ap = A1 XOR A2 XOR A3 │
│ 복구: A1 = Ap XOR A2 XOR A3 (A1 손실 시) │
│ │
│ 특징: │
│ - 패리티가 분산됨 (RAID 4는 전용 디스크) │
│ - 용량: (n-1) × 단일 용량 │
│ - 1개 디스크 고장 허용 │
│ - 읽기 속도 향상, 쓰기는 패리티 계산으로 약간 느림 │
│ - 용도: 대부분의 서버 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 6 (Dual Parity) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
│ │ 디스크 0 │ 디스크 1 │ 디스크 2 │ 디스크 3 │ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ A1 │ A2 │ Ap (P) │ Aq (Q) │ │
│ ├─────────────┼─────────────┼─────────────┼─────────────┤ │
│ │ B1 │ Bp (P) │ Bq (Q) │ B2 │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
│ │
│ 특징: │
│ - 2개의 패리티 (P, Q) - 다른 알고리즘 │
│ - 2개 디스크 동시 고장 허용 │
│ - 용량: (n-2) × 단일 용량 │
│ - 최소 4개 디스크 필요 │
│ - RAID 5보다 안전, 쓰기 성능은 더 낮음 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ RAID 10 (1+0, Mirror + Stripe) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 데이터: A B C D │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 스트라이프0 │ │ 스트라이프1 │ │
│ │ A C │ │ B D │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ┌──────┴──────┐ ┌──────┴──────┐ │
│ ▼ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 디스크0 │ │ 디스크1 │ │ 디스크2 │ │ 디스크3 │ │
│ │ A C │ │ A C │ │ B D │ │ B D │ │
│ │ (원본) │ │ (미러) │ │ (원본) │ │ (미러) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 특징: │
│ - RAID 1 (미러) + RAID 0 (스트라이프) 결합 │
│ - 성능: RAID 0에 가까움 │
│ - 안정성: RAID 1에 가까움 │
│ - 용량: 50% 효율 │
│ - 각 미러 쌍에서 1개씩 고장 허용 (최대 2개) │
│ - 용도: 고성능 + 고신뢰성 필요한 경우 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
7.3 RAID 레벨 비교¶
┌────────────────────────────────────────────────────────────────────────────────┐
│ RAID 레벨 비교표 │
├──────────┬──────────┬──────────┬──────────┬──────────┬─────────────────────────┤
│ RAID │ 최소 │ 용량 │ 읽기 │ 쓰기 │ 고장 허용 │
│ 레벨 │ 디스크 │ 효율 │ 속도 │ 속도 │ │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────────┤
│ RAID 0 │ 2 │ 100% │ n배 │ n배 │ 0개 (위험) │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────────┤
│ RAID 1 │ 2 │ 50% │ 2배 │ 1배 │ 1개 │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────────┤
│ RAID 5 │ 3 │ (n-1)/n │ 빠름 │ 보통 │ 1개 │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────────┤
│ RAID 6 │ 4 │ (n-2)/n │ 빠름 │ 느림 │ 2개 │
├──────────┼──────────┼──────────┼──────────┼──────────┼─────────────────────────┤
│ RAID 10 │ 4 │ 50% │ 빠름 │ 빠름 │ 각 쌍에서 1개 │
└──────────┴──────────┴──────────┴──────────┴──────────┴─────────────────────────┘
연습 문제¶
문제 1: inode 블록 포인터¶
4KB 블록, 4바이트 포인터를 사용하는 Unix 파일 시스템에서 100MB 파일을 저장하려면 몇 개의 간접 블록이 필요한가?
정답 보기
블록 크기: 4KB = 4096 바이트
포인터/블록: 4096 / 4 = 1024개
파일 크기: 100MB = 102,400KB = 25,600 블록
직접 블록: 12개 → 12 블록 커버
단일 간접: 1024개 → 1024 블록 커버
이중 간접: 1024 × 1024 = 1,048,576 블록 커버
필요 블록: 25,600개
직접: 12개 사용
단일 간접: 1024개 사용 (간접 블록 1개)
남은 블록: 25,600 - 12 - 1024 = 24,564개
이중 간접:
- 1차 간접 블록 수: ceil(24564 / 1024) = 24개
- 2차 간접 블록: 1개
총 간접 블록:
- 단일 간접 블록: 1개
- 이중 간접 1차: 1개
- 이중 간접 2차: 24개
총: 26개
문제 2: FAT 체인¶
FAT 테이블이 다음과 같을 때 파일 A(시작: 3)의 클러스터 체인을 나열하시오.
| 클러스터 | 값 |
|---|---|
| 0 | FREE |
| 1 | 8 |
| 2 | FREE |
| 3 | 7 |
| 4 | EOF |
| 5 | 1 |
| 6 | FREE |
| 7 | 4 |
| 8 | EOF |
정답 보기
파일 A 시작: 클러스터 3
체인 추적:
3 → FAT[3]=7 → FAT[7]=4 → FAT[4]=EOF
파일 A의 클러스터 체인: 3 → 7 → 4 → EOF
파일 크기: 3개 클러스터
참고로 클러스터 5부터 시작하는 또 다른 파일:
5 → FAT[5]=1 → FAT[1]=8 → FAT[8]=EOF
체인: 5 → 1 → 8 → EOF
문제 3: 저널링 복구¶
시스템 크래시 후 저널에 다음 상태가 있습니다. 복구 후 파일 시스템 상태는?
저널 내용:
[TxB_1] [Block 100: 데이터A] [Block 101: 메타데이터A] [TxE_1]
[TxB_2] [Block 200: 데이터B] [Block 201: 메타데이터B]
(TxE_2 없음)
정답 보기
복구 과정:
1. Transaction 1 검사:
- TxB_1과 TxE_1 모두 존재
- 완료된 트랜잭션
- Block 100, 101을 실제 위치에 다시 쓰기 (redo)
2. Transaction 2 검사:
- TxB_2만 있고 TxE_2 없음
- 미완료 트랜잭션
- 이 트랜잭션 무시 (undo)
- Block 200, 201의 변경 적용 안 함
결과:
- Transaction 1의 변경사항: 적용됨 (파일 A의 변경 완료)
- Transaction 2의 변경사항: 적용 안 됨 (파일 B 변경 없음)
파일 시스템은 Transaction 1까지만 완료된 일관된 상태
문제 4: RAID 용량 계산¶
각각 2TB 디스크 6개로 다음 RAID를 구성할 때 사용 가능한 용량은?
- RAID 0
- RAID 1
- RAID 5
- RAID 6
- RAID 10
정답 보기
디스크: 6개 × 2TB = 12TB 총 용량
1. RAID 0: 6 × 2TB = 12TB (100%)
- 모든 공간 사용
- 중복 없음
2. RAID 1: 2 × 2TB = 4TB (미러링, 3쌍)
또는 단순 2개 미러: 2TB
- 6개를 3쌍으로 미러링 → 각 쌍 2TB
- 합계: 3 × 2TB = 6TB (50%)
3. RAID 5: (6-1) × 2TB = 10TB (83%)
- 패리티에 1개 디스크분 사용
4. RAID 6: (6-2) × 2TB = 8TB (67%)
- 패리티에 2개 디스크분 사용
5. RAID 10: 6/2 × 2TB = 6TB (50%)
- 3개 스트라이프, 각각 미러
- 또는: (디스크 수 / 2) × 단일 용량
문제 5: 파일 삭제 과정¶
Unix 파일 시스템에서 /home/user/file.txt 삭제 시 일어나는 과정을 inode와 블록 관점에서 설명하시오.
정답 보기
삭제 명령: rm /home/user/file.txt
1. 경로 분석:
- /home 디렉토리의 inode 찾기
- /home/user 디렉토리의 inode 찾기
- file.txt의 inode 번호 찾기 (예: inode 12345)
2. 권한 확인:
- 디렉토리(/home/user)에 쓰기 권한 있는지 확인
- 파일이 쓰기 잠금 상태인지 확인
3. 디렉토리 엔트리 제거:
- /home/user 디렉토리 파일에서 "file.txt" 엔트리 삭제
- rec_len 조정하여 공간 병합
4. inode 링크 카운트 감소:
- inode 12345의 link_count--
- link_count > 0이면 여기서 끝 (다른 링크 존재)
5. link_count == 0이고 열린 파일 없으면:
- inode 비트맵에서 12345 해제 (0으로 표시)
- inode의 데이터 블록 포인터 확인
- 모든 데이터 블록을 블록 비트맵에서 해제
- 간접 블록도 해제
6. 슈퍼블록 업데이트:
- 자유 inode 수 증가
- 자유 블록 수 증가
주의: 실제 데이터는 지우지 않음
- 블록이 '사용 가능'으로 표시될 뿐
- 새 데이터로 덮어쓰기 전까지 복구 가능
다음 단계¶
18_IO와_IPC.md에서 I/O 시스템과 프로세스 간 통신을 배워봅시다!
참고 자료¶
- Silberschatz, "Operating System Concepts" Chapters 14-15
- Linux kernel source:
fs/ext4/,fs/fat/ - ext4 documentation: https://www.kernel.org/doc/html/latest/filesystems/ext4/
- RAID fundamentals: https://en.wikipedia.org/wiki/RAID