[운영체제] 파일 시스템의 영속성(Persistence) | Crash Consistency, FSCK, Journaling

 파일 시스템의 Persistence

우리가 프로그램을 사용할 때 가장 아찔한 순간은 저장하지 않았는데 갑자기 프로세스가 죽어버리는 상황일 것이다. 마찬가지로 OS에서 데이터를 저장하는 도중에 전원 플러그를 뽑으면 어떻게 될까? 메모리(RAM)에 있는 데이터가 날아가는 건 당연하지만, 하드 디스크에 쓰고 있던 데이터마저 깨져버린다면? 파일 시스템은 그 거대한 모순을 어떻게 견뎌내는 걸까.

오늘은 파일 시스템의 영속성(Persistence)을 위협하는 Crash Consistency(충돌 일관성) 문제와, 이를 해결하기 위한 고전적 방법인 FSCK, 그리고 현대적인 해결책인 Journaling(저널링)에 대해 알아보겠다.



1. 문제 상황: 3번의 쓰기와 1번의 크래시

파일 끝에 4KB 데이터를 추가하는 단순한 작업(append)을 생각해 보자. 우리는 코드 한 줄로 처리하지만, 파일 시스템 내부에서는 최소 3번의 물리적인 쓰기 작업(Write)이 필요하다.

Write() in detail

  1. 아이노드 업데이트 (Inode Update): 파일 크기를 늘리고 새로운 데이터 블록 포인터를 추가해야 한다.

  2. 데이터 비트맵 업데이트 (Data Bitmap Update): 새로 할당된 블록을 '사용 중'으로 표시해야 한다.

  3. 데이터 블록 쓰기 (Data Block Write): 실제 사용자 데이터를 디스크에 기록해야 한다.

문제는 디스크가 이 3가지 작업을 '동시에(Atomically)' 처리할 수 없다는 점이다. 하나씩 순차적으로 기록해야 하는데, 만약 두 번째 작업까지만 하고 전원이 나가버린다면?

  • 비트맵만 기록된 경우: 아이노드는 그 블록을 가리키지 않는데, 비트맵은 사용 중이라고 한다. -> 공간 누수 (Space Leak)

  • 아이노드만 기록된 경우: 아이노드는 블록을 가리키는데, 그곳엔 쓰다 만 쓰레기 데이터가 있다. -> 데이터 손상 (Garbage Data)

이처럼 메타데이터와 실제 데이터 간의 불일치가 발생하는 것을 Crash Consistency Problem이라고 한다.



2. 사후 수습: FSCK (File System Checker)

초기 유닉스 시스템은 "일단 문제가 생기면 나중에 고치자"라는 방식을 택했다. 그것이 바로 fsck 도구다. 시스템이 부팅될 때 파일 시스템을 검사하여 불일치한 부분을 찾아 수정한다.

  • 작동 방식: 슈퍼블록, 프리 블록, 아이노드 상태, 링크 수 등을 전수 조사한다. 예를 들어, 어떤 아이노드도 가리키지 않는 할당된 블록(공간 누수)을 발견하면 비트맵을 수정하여 다시 사용 가능하게 만든다.

  • 치명적인 단점: 너무 느리다. 디스크 용량이 커질수록 검사 시간은 기하급수적으로 늘어난다. 수 테라바이트의 디스크를 검사하려면 몇 시간이 걸릴 수도 있다.

"FSCK는 훌륭한 도구지만, 현대의 대용량 스토리지 환경에서는 더 이상 유효한 전략이 아니다."


 

3. 사전 예방: Journaling (Write-Ahead Logging)

데이터베이스 시스템에서 아이디어를 빌려온 Journaling(저널링)은 접근 방식 자체를 바꿨다. "쓰기 전에, 무엇을 할지 먼저 적어두자(Write-Ahead Logging)."

3.1 저널링의 핵심 프로토콜

리눅스 ext3 파일 시스템의 데이터 저널링 모드를 기준으로, 데이터를 쓰는 과정은 다음과 같이 정교해진다.

  1. Journal Write: 트랜잭션의 시작(TxB), 메타데이터, 그리고 실제 데이터를 로그(Journal) 영역에 먼저 쓴다.

  2. Journal Commit: 로그 작성이 완료되었음을 알리는 '종결 블록(TxE)'을 쓴다. 이 단계가 성공해야만 트랜잭션이 유효한 것으로 인정된다.

  3. Checkpoint: 이제 안심하고 실제 디스크 위치(아이노드, 비트맵, 데이터 영역)에 데이터를 덮어쓴다.

  4. Free: 체크포인트가 끝나면, 로그 영역의 해당 트랜잭션은 더 이상 필요 없으므로 삭제(재사용) 표시를 한다. (즉, Circular data structure를 활용한다)

만약 Checkpoint 도중에 전원이 꺼지더라도, 재부팅 시 로그(Journal)만 읽어서 다시 실행(Replay)하면 된다. 전체 디스크를 뒤질 필요가 없어 복구 속도가 획기적으로 빠르다.

Jounal (이전 리눅스 파일 시스템은 지난 시간 참고)


3.2 원자성(Atomicity)을 위한 디테일

여기서 흥미로웠던 점은 Journal WriteJournal Commit을 분리하는 이유다. 디스크는 내부적으로 쓰기 순서를 최적화(Scheduling)하기 때문에, 만약 한 번에 다 보내버리면 TxE(종결 블록)가 데이터보다 먼저 기록될 수 있다. 그 상태에서 크래시가 나면, 쓰레기 데이터가 유효한 트랜잭션인 것처럼 오인될 수 있다. 그래서 반드시 데이터가 로그에 안전하게 박힌 것을 확인한 뒤에야 TxE를 기록한다.

TxB & TxE



4. 저널링의 모드와 트레이드오프

모든 데이터를 저널링하면 너무 느리지 않을까? 그래서 ext4 같은 파일 시스템은 성능과 안정성 사이에서 타협점을 제공한다.

  • Data Journaling (Journal 모드): 데이터와 메타데이터를 모두 로그에 쓴다. 가장 안전하지만 가장 느리다. (데이터를 두 번 쓰는 셈이다)

  • Ordered Journaling (기본값): 메타데이터만 로그에 기록한다. 단, 메타데이터를 로그에 쓰기 전에 실제 데이터 블록을 먼저 디스크에 쓴다. 순서를 강제함으로써, 아이노드가 쓰레기 데이터를 가리키는 문제를 방지한다. 성능과 안전성의 균형이 가장 좋다.

  • Writeback Journaling: 메타데이터만 로그에 기록하고, 데이터 쓰기 순서는 신경 쓰지 않는다. 빠르지만, 크래시 발생 시 파일 내용이 깨질 위험이 있다.



마치며

Crash Consistency 문제를 공부하며 신뢰성을 만들기 위한 OS 개발자들의 노력을 확인할 수 있었다. FSCK가 무식하게 전체를 뒤지는 방식이었다면, Journaling은 '미리 기록하는 비용'을 지불하고 '빠른 복구'를 얻는 전략이다. 상황에 맞춰 어떤 모드의 저널링을 사용할지, 혹은 성능을 위해 어디까지 위험을 감수할지 판단할 수 있어야 한다.

결국 시스템 엔지니어링은 일어날 수 있는 최악의 상황(Crash)을 가정하고, 그 속에서도 정합성(Consistency)을 잃지 않으려는 치열한 고민의 산물이다.




추천글:

[운영체제] Operating System 전체 포스팅 모음집

[운영체제] 파일 시스템과 디렉토리 | Unix File System, Inode, Storage Virtualization

[운영체제] 파일 시스템 구현(VSFS) | Index Node(Inode) in deep

[운영체제] Fast File System (FFS) | 디스크의 물리적 구조를 고려한 성능 최적화


hyeon_B

안녕하세요! AI 기술을 이용해 더 나은 세상을 만들어 나가고 싶은 과기원생 Hyeon이라고 합니다. 저는 앞으로 인공지능 시대에는 지식을 '활용'하는 능력이 중요해질 것이라고 생각합니다. 대부분의 일들은 인공지능이 뛰어난 모습을 보이지만, 인공지능은 데이터로 부터 연관관계를 학습하기 때문에 지식들을 새로 통합해서 활용하는 능력이 부족합니다. 인공지능이 뉴턴 전에 만들어졌다면 사과가 떨어지는 이유에 대답하지 못했을 것이고, 아인슈타인 전에 만들어졌다면 중력이 어떻게 생기는지 설명하지 못했을 것입니다. 따라서 앞으로 우리는 '본질'을 탐구하고 그 본질로부터 다른 곳에 적용하며 인공지능을 현명하게 활용해야 할 것입니다. 함께 인공지능 시대를 준비합시다!

댓글 쓰기

다음 이전

POST ADS1

POST ADS 2