본문 바로가기

Programming/General

Crash Consistency를 유지하기 위한 방법 (fsck 와 journaling 의 자세한 소개)

얼마전에 소개한 Operating Systems: Three Easy Pieces 책에서 fsck 와 journaling 에 대해 소개한 챕터를 전체 번역하고 요약하였습니다.

개인적으로 이 부분에 대한 지식이 필요한 사람들에게 크게 유용한 자료가 될 것이라 생각합니다.


특히, filesystem 의 journaling mode 들에 동작에 대해서, 비교적 상세하게 기술하였기 때문에 이 부분을 이해하는데 큰 도움이 되었습니다.


원문 문서 링크 : http://pages.cs.wisc.edu/~remzi/OSTEP/file-journaling.pdf

번역본 전체 : 

Crash Consistency FSCK and Journaling - interpretation korean.pdf


※ 사용하시면 꼭 출처를 밝히시기 바랍니다.



<< fsck 를 이용한 consistency 보장 >>

- 오래 사용하던 방식임.

- 파일시스템이 in-consistency 한 상태가 되도록 놔두었다가 재 부팅시 디스크 전체를 검사하고 오류를 수정하는 방식임.

- 현대에 디스크 사이즈가 커지고 RAID 가 등장함에 따라, 수행 시간이 너무 오래걸려서 사용하기 어려움.


<< jorunaling 을 이용한 consistency 보장 >>

- 디스크는 512 byte block write 에 대해 atomic 을 보장함.

- 써지거나, 그렇지 않거나.. 반만 써지는 일은 없음.

[by Crash consistency : FSCK and Journaling 의 11 page]


< 일반적인 수정 without journal >


data Da 를 가지고 있는 file 이 file system에 저장되어 있는 상태

B[v1] : Data Bitmap version 1

I[v1] : Inode version 1

Da : Data block version a



이때의 I[v1] 의 상태








file의 크기를 변경하는 수정을 함.

I[v1] -> I[v2] 로 수정됨

B[v1] -> B[v2] 로 수정됨

Db 라는 추가 data block 이 붙어서 파일의 크기가 확장됨.



이때 inode 의 변경 사항





< Ext4 data journal mode >


= I[v1], B[v1], Da 가 file system structure 에 저장되어 있다.

= file의 크기를 변경하는 수정을 시도 시작 (위의 일반적인 수정과 동일한 수정을 시도함)

= directory 의 경우 directory 의 data 자체도 metadata 취급을 함. [Crash consistency : FSCK and Journaling 의 15 page]


1. journal write

TxB, metadata, data 를 포함하는 transaction 의 contexts 들을 log 에 쓴다. 이것이 완료되는것을 기다린다. (file system journal 에 TxE 를 제외한 TxB, I[v2], B[v2], Db 를 write 함. 이때 모두다 한꺼번에 write 를 issue 함)



TxB : Transaction begin

       update 에 대한 정보

       I[v2], B[v2]와 Db 의 마지막 주소등

       TID(transaction identifier)


TxE : Transaction End

       TID(transaction identifier)


2. journal commit

transaction commit block(TxE 를 포함하는) 을 log 에 쓴고, 완료되는것을 기다린다. 이후에 transaction 은 committed 되었다고 한다. (file system journal 에 TxE write 를 issue 함.)




3. checkpoint

update 된 metadata 와 data의 contect들을 disk 의 최종 위치에 write 한다.


4. free

얼마후에, (main file system superblock 과 혼동하지 말자),journal superblock 의 갱신에 의해 journal 안에 transaction free 가 mark 된다.





(주** : 아랫방향으로 시간이 증가한다.)

(주** : 점선으로 나뉘어진 부분사이의 order 는 지켜져야 한다. 점선 안의 transection 의 order 는 상관 없다.)


< Ext4 data journal mode - Recovery >

= 크래쉬가 TxE 가 쓰기 전에 발생하면, 일은 쉽다. file structure(disk 의 final location)에 pending update(metadata , data 를 write 하는 일) 수행을 단순히 skip 한다.


= 크래쉬가 commit 뒤에 checkpoint 전에 났을 경우

시스템이 부팅할때, 파일 시스템 리커버리 프로세스가 로그를 스캔하고 디스크에서 커밋된 트랜젝션을 살펴본다. 이 트랜젝션은 순서대로 replay 된다. 그리고 이 블럭들은 그들의 최종 디스크 위치에 write 를 시도한다. 이 로깅 방식은 가장 단순한 방식중에 하나이고, redo logging 이라고 부른다. 


= 크래쉬가 checkpointing 중 어느 시기에 발생하더라도 괜찮다는것에 주목해라. 심지어 블럭중 몇몇이 최종 위치에 업데이트가 된 이후에라도.

최악의 상황에, 이 업데이트중 몇몇은 리커버리 동안에 다시 수행된다.


< Metadata Journaling >


이 프로토콜은 다음을 따른다.


1. Data write

최종 파일시스템 위치에 data(Db) 를 filesystem structure 에 write 하고 완료를 기다린다. (대기는 옵션이다.)

2. Journal metadata write

TxB 와 I[v2], B[v2] (meta data) 를 journal log 에 쓰고, 완료되기를 기다린다.

3. journal commit

TxE 를 포함하는 transaction commit block 을 journal log 에 쓰고, 완료를 대기한다. Transaction (data 를 포함한) 은 이제 commit 되었다.

4. checkpoint metadata

I[v2], B[v2](metadata) 를 파잀스템 안에 그들의 최종 위치에 write 한다. 

5. Free

후에, journal superblock 안의 transaction free 가 mark 된다.




(주** : 아랫방향으로 시간이 증가한다.)

(주** : 점선으로 나뉘어진 부분사이의 order 는 지켜져야 한다. 점선 안의 transection 의 order 는 상관 없다.)


최종적으로 (위의)  journal 에 쓰기가 issue 되기 전에 step1 인 data 쓰기가 완료되는 것을 강제하는 것에 주목해 보자.

(step2 - metadata journal write) 는 위의 프로토콜이 지시하는 것처럼 정확함을 요구하지 않는다. 특히, data write 뿐만아니라, TxB 와 metadata  가 issue 되는것이 괜찮다.

정말  중요한 요구사항은, step 1과 step 2 과 step 3 이전에 완료 되어야 한다.

[Crash consistency : FSCK and Journaling 의 15 page]


“write the pointed to ob- ject before the object with the pointer to it”

정말로 "포인트되는 object 를 그것을 가르기는 object 전에 써라" 라는 룰이 crash consistency 의 핵심룰이다.



< delete 시의 journal 여부 >


1. foo 라는 directory 가 있음.

   filesystem 은 metadata journal mode 임.

   foo directory 의 data 는 block 1000 에 저장되어 있음.

2. 사용자가 foo directory 안에 새로운 파일을 만들었음.

   directory 의 data 도 metadata 이기 때문에, journal log 에 commit 됨.

   


3. 사용자가 foo directory 를 삭제함. (주** 이 설명에서는 delete 할때는 journal 되지 않는다고 가정함. 실제로는 그렇지 않음.)

4. 새로운 foobar 라는 파일을 생성함.

    foorbar 의 data 는 삭제된 foo 와 같은 block 1000 에 할당되어 아래와 같이 journal log 에 commit 됨.

    metadata journal mode 이기 때문에 foobar 의 data 는 journal log 에 commit 되지 않음.



5. 이 상태에서 system crash 가 발생함

   reboot 시 system 은 recovery 를 시도하고, 위의 journal log 가 replay 되면서 D[foo] 가 foobar 의 내용을 덮어쓰게 되는 문제가 발생함.


결론 : 이러한 문제를 해결하기 위해 몇가지 방법을 사용함.

       - 방법 1 : 삭제가 checkpopint 되기 전에 사용했던 block 을 reuse 하지 않음.

       - 방법 2 : revork 라는 record 를 만들어서, 삭제된 디렉토리나 파일이 있을때 revork recode 를 journal log 에 commit 함. (ext3 가 사용하는 방법임)



< consistency 를 보장하기 위한 다른 방법들 >


- Soft Update

이러한 방법중 하나는 "Soft Update" 로 알려져 있고 Ganget and Patt 에 의해 소개되었다. 이 방법은 파일시스템에 모든 write 를 조심스럽게 ordering 한다. 디스크 구조가 절대 inconsistent 상태로 남아있지 않도록 확인하기 위해서 이다. 예를 들면, 포인트가 되는 데이터 블럭은 그것을 포인트 하는 inode 보다 먼저 쓰는 방법으로, 우리는 inode 가 절대 쓰래기값을 포인트 하지 않는것을 확인할 수 있다.; 유사한 방법으로 파일시스템의 모든 구조를 운용할 수 있다. Soft Update들의 구현은 도전적이다. 그러나 위에 묘사한 저널링 레이어는  비교적 정확환 파일 시스템 구조의 작은 이해만으로도 구현할 수 있다. Soft Update는 각각의 파일시스템 데이터 구조에 대한 복작한 지식을 요구하고, 시스템에 그정도의 복잡성을 더한다.


- copy-on-write(COW)

또다른 방법은 copy-on-write(COW)로 알려져 있고, 많은 유명한 파일시스템이 사용하고 있다. Sun 의 ZFS 를 포함해서.. 이 기술은 제자리에 있는 파일이나 디렉토리를 절대 overwrite하지 않는다. 더 정확히 말하면, 새로운 업데이트를 디스크에 사전에 준비된 사용되지 않은 영역에 업데이트 한다. 많은 업데이트들이 완료된 이후에, COW 파일 시스템은 새롭게 업데이트된 structure들을 가르키는 포인터가 포함된 파일 시스템의 root structure들을 뒤집는다. 이 실행은 복잡하지 않은 파일 시스템의 consistent 를 유지하도록 만든다. 우리가 다른 챕터에서 LFS(log-structured file system)에 대해 논의할때 이 기술에 대해서 좀 더 많이 배울 것이다.


- BBC(backpointer-based consistency)

다른 방법은 Wisconsin 에서 개발된 것이다. 이 기술은 BBC(backpointer-based consistency)라고 이름지어졌다. write 사이에 ordering 은 강요되지 않는다. consistency 를 만족하기 위해서, 부가적인 back pointer(주** 예를 들면 data 자신을 가르키 indoe 가 있다면 data 의 back point 는 이 inode 를 가르킨다)는 가 시스템의 모든 block 에 추가된다. 예를 들면, 각각의 데이터 블럭은 그것이 속하는 inode 를 위한 참조를 가진다. 파일을 엑세스할때, 파일 시스템은 그 파일이 checking에 의해서 consistent 한지 결정할 수 있다. 그 checking 은 앞의 포인터(예를 들면, inode 나 direct block 안의 주소값)이 그것을 위한 백을 참조하는 블럭을 가르키는지 아닌지를 본다. 만약 그렇다면, 모든것은 안전하게 이스크에 도달하고 파일은 consistent 하다. 만약 그렇지 않다면, 파일은 inconsistent 하고, error 가 리턴된다. back pointer 를 파일시스템에 추가함으로써, lazy crash consistency의 새로운 방법이 이루어 질 수 있다.