본문 바로가기

CS

크래시 일관성 : FSCK와 저널링

이번장은 파일 저장 중 크래시가 발생했을 때 안전하게 갱신할 수 있는지에 대한 질문에 답을 구하는 시간이다. 예를 들어 디스크 상에 A와 B 가 있는데 둘 중에 하나를 처리하다가 크래시가 난 경우 일관성이 깨지게 된다. 이게 크래시 일관성 문제. 여기서는 이 문제를 해결하는데 fsck와 저널링(write-ahead-logging)이라는 기법을 살펴볼 것이다. 다음과 같은 예제를 살펴보자

 위의 상태에서 내용을 새로 추가하는데 그럼 디스크에 세 번의 쓰기를 더 수행해야 한다. 아이노드와 비트맵 그리고 블록에 그럼 다음과 같은 결과가 나와야 한다.

자 여기서  크래시가 발생해서 생길 수 있는 시나리오는 여러가지다. 다음과 같다

첫 번째 해결책으로는 파일 시스템 검사기 fsck라는 도구를 사용한다. 여러 일들을 할 수 있는데 문제는 너무 오래 걸린다는 것이다.  또한 세 개의 블록을 갱신하다 생긴 문제를 해결하기 위해 디스크를 전체 다 읽어본다는 것은 비용이 크다. 따라서 다른 방법을 보자.

두 번째는 저널링(write-ahead-logging)이라는 방식이다. 이 해법은 DBMS에서 차용한 개념 중 하나다. 기본 개념은 다음과 같다. 디스크 내용을 갱신할 때, 해당 자료구조를 갱신하기 전에, 먼저 수행하고자 하는 작업을 요약해서 기록해 둔다(디스크에). 기록 방법도 다양하다. log라는 자료구조에 저장한다. 나중에 디스크 페이지들을 새 값으로 갱신하는 과정에서 크래시가 발생하면, 로그를 확인해서 다시 갱신하면 된다. 이를 redo라 한다. 쓰기를 할 때는 로깅을 한번 거치기 때문에, 약간 느려진다.  이제 실제로 저널링이 어떻게 쓰이는지 알아보자. 데이터 저널링을 먼저 보겠다. 먼저 아이 노드와 비트맵 그리고 데이터 블록을 갱신하는 연산을 보자

위에서 보듯이 트랜잭션의 시작 블럭은 연산에 대한 정보들을 기록한다. 여기에 트랜잭션 식별자와 같은 것이 있다. 가운데 3개의 블록에는 디스크에 저장될 내용들이 있다. 마지막 종료 블록은 이 트랙잭션의 종료를 알리며 마찬가지로 TID를 포함하고 있다. 트랜잭션 종료 블록이 로그에 기록되면 트랜잭션은 커밋되었다고 말한다. 트랜잭션이 디슼크에 안전히 기록된 후, 파일 시스템 상의 자료 구조들은 이제 갱신될 수 있다. 저널에 기록된 내용을 실제 위치에 반영하는 과정을 체크 포인팅이라 부른다. 체크포인트 시에 앞에서 본 것과 같이 디스크의 원래의 위치에 위의 정보들을 쓰도록 요청함. 이 쓰기 요청이 성공적으로 완료되면 최종적으로 동작이 끝난 것이다. 체크포인트를 저널에 기록한 직후에 바로 진행하지는 않는다. 그런데 여기서 문제는 저널에 기록하는 중에 크래시가 발생하게 되면 일이 복잡해진다. 위의 예제에서 트랜잭션에 속한 블록 집합을 차례대로 요청하면 너무 느리고, 한 번에 보내면 이게 디스크에서 어떻게 스케줄링될지 모른다.

트랜잭션 종료 블럭이 먼저 쓰이고 데이터 블록이 써지기 전에 크래시 나면 알 수 있는 방법이 없다. 따라서 트랜잭션을 두 단계로 나누어 기록한다. 트랜잭션 종료 블록을 제외한 나머지 모든 블록을 한 번의 쓰기 요청으로 저널에 쓴다. 그 뒤 완료되면 트랜잭션 블록에 대한 쓰기를 요청하여 저널 작성을 완료한다. 트랜잭션 종료 블록은 무조건 원자적으로 기록되어야 한다. 다음은 크래시 상황에서 저널을 사용하여 파일 시스템을 복구하는 방법을 보자. 만약 트랜잭션이 로그에 안전하게 기록되기 전에 크래시가 발생한다면 복구 시에 아무것도 안 하면 된다. 트랜잭션이 로그에 기록되었지만, 체크포인트가 완료되기 전에 크래시가 발생한다면 부팅할 때 로그를 탐색해서 커밋된 트랜젝션을 파악하고, 그걸 기반으로 디스크 상에 원래의 위치에 쓴다. 이 방식을 redo logging이라고 한다.  로그 기록도 batch 방식으로 하는 게 디스크에 발생하는 많은 트래픽을 해결할 수 있다. 로깅해야 할 모든 파일 시스템 갱신 내용을 트랙젝션 버퍼라는 자료 구조에 보관한다. 파일 시스템 파티션마다 하나만 존재하는 전역 자료구조이다. 저널 버퍼라고 불리기도 한다. 위의 예제와 같이 두 개의 파일이 생성되는 경우 메모리에 존재하는 아이노드 비트맵, 파일들의 아이 노드들, 디렉터리 데이터, 그리고 디렉터리 아이 노드를 갱신됨(dirty)으로 설정하고 해당 블록 들을 트랜젝션 버퍼 리스트에 추가한다.  트랜젝션 버퍼의 내용들은 파일 시스템이 저널을 커밋할 때 디스크에 기록된다. 

 로그 공간의 관리 

로그의 크기는 정해져있다. 트랜젝션이 계속 추가되면 얼마 지나지 않아 공간이 소진될 것이다. 로그 공간이 가득 차면 두 가지 문제가 발생함. 첫 번째 문제는 간단하다. 로그에 있는 모든 트랜젝션을 순서대로 재실행해야 하기 때문에 로그가 커질수록 복구 소요 시간은 길어짐. 두 번째 문제는 로그가 가득 차서 디스크에 더 이상의 트랜젝션을 커밋할 수가 ㅇ벗기 때문에 파일 시스템을 갱신하는 모든 작업들이 실패함. 이 문제를 해결하기 위해서 저널링 파일 시스템은 로그를 환형 자료 구조 형식으로 사용함. 파일 시스템은 트랜젝션이 체크포인트 되면 해당 로그 공간이 재사용될 수 있도록 트랜젝션이 차지하고 있던 공간을 비운다. 저널 슈퍼블록에 트랜젝션 위치를 기록해 두어서, 최신 트랜젝션과 가장 오래된 트랜젝션의 위치를 기록해 놓는다.

메타데이터 저널링 

복구 시간은 단축했지만 저널에 먼저 기록해야 되서 쓰기 양이 두배로 되었다. 그래서 데이터 블록을 저널에 넣지 않고 메타데이터만 로그에 기록한다 이걸 Ordered journaling , 메타 저널링 이라고도 한다. 여기서 중요한 것은 데이터 블록을 언제 디스크에 보내냐는 건데 크래시가 나버리면 문제가 생길 수 있기 때문에  데이터 블록을 먼저 디스크에 보낸다. 다른 사례를 한번 보자 블록 재활용에 관한 문제다. 다음 같이 foo 디렉터리 데이터 블록이 1000번에 위치한다고 해보자. 

이 시점에서 사용자가 디렉터리의 모든 파일을 디렉터리 자체와 삭제했고 1000번은 free가 되었다.  그 뒤 새로운 파일 foobar의 아이노드가  디스크에 저장된다. 아직 데이터 블록은 저널에 기록되지 않았다. 이 상황에서 크래시가  발생하면 다시 복구할 때 foobar의 파일을 읽으면 엉뚱한 값이 나올 것이다. 여기서 해결책은 저널에 철회 레코드를 추가해서 디렉터리를 삭제하면 저널에 철회 레코드를 기록하고, 재실 행시 여기서 존재 여부를 탐색하고 철회된 내용은 재실행을 하지 않은 식으로 문제를 회피함. 마지막으로 흐름도를 보자 위에서 아래로 시간 순이다.

데이터 저널링
메타데이터 저널링

 

'CS' 카테고리의 다른 글

Pintos Project 4: File Systems  (0) 2021.10.29
파일 시스템 구현  (0) 2021.10.26
RAID  (0) 2021.10.26
하드 디스크 드라이브  (0) 2021.10.25
입/출력 장치 개념  (2) 2021.10.25