일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 영속성
- DirectX 12
- 디자인패턴
- 타입 객체
- DirectX12
- 알고리즘
- OS
- 동적계획법
- 멀티프로세서
- 그리디 알고리즘
- 프로그래머스
- 쓰레드
- I/O장치
- 멀티쓰레드
- 락
- 자료구조
- 운영체제
- 컨디션 변수
- 백준
- Direct12
- 병행성
- codility
- 다이나믹프로그래밍
- 파일시스템 구현
- 병행성 관련 오류
- directx
- 스케줄링
- 다이나믹 프로그래밍
- 그리디알고리즘
- 렌더링 파이프라인
- Today
- Total
기록공간
4-6장. 크래시 일관성 : FSCK와 저널링 본문
여타 자료 구조들과는 다르게 파일 시스템의 자료 구조는 안전하게 저장되어야 한다. 즉, 장시간 사용 후에도 유지되어야 하며 전력 손실에도 하드 디스크나 플래시 기반 SSD 장치의 데이터는 손상 없이 유지되어야 한다.
파일 시스템이 가진 가장 큰 어려움은 전력 손실이나 시스템 크래시가 발생하는 상황에서도, 어떻게 안전하게 디스크 상의 내용을 갱신하는가에 대한 문제이다. 전력 손실이나 크래시 때문에 디스크 상의 자료 구조를 안전하게 갱신하는 것은 상당히 까다로운 작업이 된다. 파일 시스템은 크래시일관성(crash-consistency)
이라는 새롭고 흥미로운 문제에 직면하게 된다.
문제를 이해하는 것은 어렵지 않다. 어떠 특징 작업을 위해 디스크 상에서 두 개의 자료 구조 A와 B를 갱신해야 한다고 해 보자. 디스크는 한 번에 하나의 요청만 처리할 자료 구조 A와 B를 갱신해야 한다고 해 보자. 디스크는 한 번에 하나의 요청만 처리할 수 있기 때문에 두 요청 중 하나의 요청이 먼저 디스크에 도달할 것이다. (A or B) 하나의 쓰기 작업만 완료한 상태에서 시스템 전원이 나간 경우, 디스크 상의 자료구조는 일관성이 깨지게 된다. 이러한 특성 때문에, 파일 시스템에서는 이제까지는 없었던 새로운 문제를 직면한다. 크래시 일관성 문제이다.
예제
저널링을 이해하기 위해 여제 하나를 살펴보자. 디스크 상에서 여러 개의 자료 구조를 갱신하는 연산을 예로 들겠다. 워크로드는 기존 파일에 블럭을 하나 추가하는 연산이다. 파일을 열고 lseek()로 파일의 끝으로 오프셋을 이동한 후에 4KB를 쓰고, 파일을 닫는다.
이전에 사용했던 간단한 파일 시스템 자료 구조를 가정한다. 이 예제에서는 아이노드 비트맵, 데이터 비트맵, 아이노드 그리고 데이터 블럭이 존재한다. 파일 시스템의 구조는 다음과 같다.
아이노드 테이블을 보면 2번 아이노드가 할당되어 있다. 아이노드 비트맵에서 세 번째 비트가 설정되어 있다. 이 파일은 4번째 데이터 블럭을 사용하고 있다. 데이터비트맵의 4번째 비트가 사용 중으로 표시되어 있다. 아이노드는 첫 번째 버전이기 때문에 I[v1]으로 표시되어 있다.
아이노드의 구조를 살펴보도록 하자. I[v1] 내부는 다음과 같다.
파일의 크기는 1이고 첫 번째 직접 포인터는 4번 블럭을 가리키며, 나머지 직접 포인터들을 null로 설정되었다.(사용하고 있지 않음)
이 파일의 끝에 내용을 추가한다는 것은 새로운 데이터 블럭을 추가하는 것이다. 세 개의 디스크 자료 구조를 갱신해야 한다. 아이노드와 새로운 데이터 블럭 Db, 그리고 데이터 비트맵(B[v2]라 하자)이다. 새로운 블럭은 사용 중이 된다.
데이터 비트맵(B[v2])은 00001100으로 갱신되었다. 마지막으로 사용자의 내용이 저장된 데이터 블럭(Db)이 있다. 이들이 디스크에 성공적으로 기록되면, 디스크의 모습은 아래와 같다.
위와 같은 형태가 되기 위해서 파일 시스템은 디스크에 세 번의 쓰기를 수행해야 한다. 하나는 아이노드(I[v2]), 그다음은 비트맵(B[v2]), 그리고 데이터 블럭(Db) 쓰기이다. write()의 결과는 디스크에 즉시 반영되지 않는다. 대신 변경된 아이노드와 비트맵 그리고 새로운 데이터는 일정 기간 동안 메인 메모리 상에 존재하다가(페이지 캐시나 버퍼 캐시) 파일 시스템이 실제로 디스크를 실행할 때 기록된다. 하지만 크래시 발생으로 인해서 디스크 기록 과정이 엉망이 될 수 있다. 구체적으로 살펴보기 위해 세 개의 쓰기 중에 하나나 두 개의 쓰기만 실제로 있었다고 해 보자. 파일 시스템은 이상항 상태에 놓이게 될 것이다.
크래시 시나리오
더 잘 이해하기 위해 클래시 시나리오를 예로 들어보자. 한 번의 쓰기만 성공한 경우를 생각해 보자. 다음 세 가지 경우가 있을 수 있다.
-
데이터 블록(Db)만 디스크에 기록됨. 이 경우에 데이터는 디스크에 있지만 그것을 가리키고 있는 아이노드가 없으며 할당 여부를 나타내는 비트맵도 없다. 이러한 경우는 파일 시스템 크래시 일관성 측면에서는 문제가 없다.
-
갱신된 아이노드(I[v2])만 디스크에 기록됨. 이 경우에는 Db가 기록되려던 디스크의 주소 (5)를 아이노드가 가리키고 있지만 아직 Db는 그 자리에 기록되지 않았다. 그러므로, 포인터를 그대로 읽는다면 디스크에서 의미 없는 데이터를 얻게 된다. (기존의 디스크 주소 (5)에 있던 데이터)
더 나아가 파일 시스템의 "일관성 손상" 이라는 새로운 문제를 만난다. 디스크 상의 비트맵은 데이터 블록 5번이 할당되어 있지 않다고 하지만 아이노드는 할당되어 있다고 한다. 파일 시스템의 자료 구조들 간의 이러한 불일치로 파일 시스템 전체의 일관성이 손상되게 된다. 파일 시스템을 사용하기 위해서는 이러한 문제를 어떤 식으로든 해결해야 한다.
-
갱신된 비트맵(B[v2])만 디스크에 기록됨. 이 경우의 비트맵은 블록 5번이 할당되었다고 표시하지만 아이노드가 가리키고 있는 블럭은 없다. 그러므로 파일 시스템의 일관성이 손상된 상태가 되었다. 해결하지 않고 그대로 둔다면 블록 5번 파일 시스템에서 사용될 수 없다.
-
데이터(Db)를 제외한 아이노드(I[v2])와 비트맵(B[v2])은 디스크에 기록됨.
이 경우에 파일 시스템 메타데이터의 일관성은 보장된다. 아이노드의 포인터는 블록 5번을 가리키고 비트맵은 5번이 사용 중을 나타내므로 파일 시스템의 메타데이터의 관점에서 보면 모든 것이 괜찮아 보인다. 한 가지 문제는 5번 블록에는 의미 없는 값이 들어 있다는 것이다.
-
비트맵(B[v2])을 제외한 아이노드(I[v2])와 데이터(Db)는 디스크에 기록됨.
이 경우에는 디스크의 데이터를 아이노드가 제대로 가리키고 있지만 이전 버전의 비트맵과 아이노드 간의 내용이 일관성이 없다. 그러므로 파일 시스템을 새로 시작하기 전에 이 문제를 해결해야 한다.
-
아이노드(I[v2])를 제외한 비트맵(B[v2])과 데이터(Db)는 디스크에 기록됨.
이 경우는 아이노드와 비트맵 간의 내용이 일치하지 않는다. 블럭이 기록되고 비트맵은 사용 중이라고 되어 있지만 아이노드가 이 파일을 가리키고 있지 않기 때문에 해당 블록이 어느 파일에 속한 것인지 알 길이 없다.
크래시 일관성 문제
크래시 때문에 파일 시스템 디스크 상의 자료 구조에 많은 문제가 발생할 수 있다는 것을 보았다. 파일 시스템 자료 구조 간의 불일치가 있을 수 있으며, 공간 누수가 발생할 수 있고, 사용자에게 의미 없는 데이터가 전달되는 등의 여러 문제가 있다. 우리는 파일 시스템의 일관성이 항상 유지되도록 만들고자 한다. 연산 이전 상태에서 연산 이후의 상태로 이동할 때, 중간의 임시 상태(transient state)로 파일 시스템이 남아 있는 경우를 방지하는 것이 목적이다. 불행하게도 디스크는 한 번에 하나의 쓰기 작업을 처리할 수 있으며 이러한 작업들 중간에 크래시나 전력 손실이 발생할 수 있어서 목적을 쉽게 달성할 수가 없다. 이러한 일반적인 문제를 크래시 일관성 문제라고 부른다. (일관된 갱신 문제라 부르기도 한다)
해법 1 : 파일 시스템 검사기
초기 파일 시스템은 크래시 일관성을 해결하기 위해 간단한 방법을 사용하였다. 기본적으로 파일 시스템이 일관성이 없더라도 그대로 두었다가 리부팅 시에 일관성 문제를 해결하는 방식을 선택하였다. fsck라고 하는 도구가 이 접근 방식의 고전적인 예이다. fsck는 일관성 불일치를 발견하고 수정하는 UNIX 도구다. 다른 시스템에도 디스크 파티션을 검사하고 고치는 도구들이 있다. 다만 이 방식이 문제들을 전부 해결할 수 없다는 것에 유의해야 한다. 위에서 살펴보았던 경우들 중에 아이노드가 의미 없는 블럭을 가리키고 있지만, 파일 시스템은 일관성이 있는 것처럼 보였던 경우가 그런 예이다. 이 도구들의 목적은 파일 시스템 메타데이터들 간의 일관성을 유지하는 것이다.
fsck의 동작은 여러 단계로 구성된다. 이것은 파일 시스템이 마운트 직전에 실행된다. 종료가 되면 디스크 상의 파일 시스템이 일관성을 갖게 되며 사용자가 사용할 수 있게 된다.
fsck가 하는 기본적인 일을 아래와 같이 정리할 수 있다.
-
슈퍼블럭 : fsck는 먼저 슈퍼블럭 내용에 오류가 없는지를 검사한다. 대부분의 검사는 파일 시스템에 블럭 개수가 파일 시스템의 크기보다 더 큰지와 같은 기초 검사들로 이루어져 있다. 이러한 검사를 통해 (손상이) 의심되는 슈퍼블럭이 있는지 찾아내는 것이다. 발견하면 시스템은 슈퍼블럭을 사본으로 대체할지를 결정한다.
-
프리 블럭 : 그다음으로 fsck는 아이노드와 간접 블럭, 이중 간접 블럭 등을 살펴보고 파일 시스템에 현재 어떤 블럭들이 할당되었는지에 대한 정보를 생성한다. 얻은 정보를 토대로 정확한 할당 비트맵을 재구성한다. 기존의 비트맵과의 일치하지 않으면 아이노드 정보를 기반으로 불일치를 해결한다. 모든 아이노드에 대해서 같은 검사를 수행하여 아이노드 비트맵의 유효성을 검사하고, 필요 시 아이노드 비트맵을 재구성한다.
-
아이노드 상태 : 각 아이노드가 손상되었는지 다른 문제는 없는지 검사한다. 예를 들어 fsck는 각 할당된 아이노드가 유효한 속성(일반 파일, 디렉터리, 심볼릭 링크 등)을 갖고 있는지 확인한다. 만약 아이노드의 항목 중에 쉽게 해결이 불가능한 문제가 존재하면, fsck는 해당 아이노드를 의심 대상으로 간주하고 초기화한다. 아이노드 비트맵도 그에 따라 갱신된다.
-
아이노드 링크 : fsck는 각 할당된 아이노드의 링크 개수를 확인한다. 링크의 개수는 특정 파일에 대한 참조를 포함하고 있는 디렉터리들의 수를 나타낸다. 링크 개수를 확인하기 위해서 fsck는 루트 디렉터리를 시작으로 모든 디렉터리 트리를 탐색하여 파일 시스템의 모든 파일과 디렉터리에 대한 링크 개수를 직접 수집한다. 새롭게 계산된 개수와 아이노드에서 확인한 수와 다른 경우가 있다면, 일반적으로 아이노드의 개수 필드를 고치는 방식으로 수정한다. 만약 할당된 아이노드는 있지만 어떤 디렉터리도 이를 참조하지 않으면, 그 파일은 lost+found 디렉터리로 이동된다.
-
중복 : fsck는 중복된 포인터가 있는지도 검사한다. 즉, 같은 블럭을 가리키는 서로 다른 아이노드가 있는지를 검사한다. 만약 한 아이노드가 누가봐도 불량이면 초기화한다. 대안으로는 참조되고 있는 블럭의 사본을 만들어서 원하는 대로 각 아이노드가 하나씩 자신의 블럭을 가리키도록 하는 방법도 있다.
-
배드 블럭 : 모든 포인터 목록을 검사하면서 배드 블럭 포인터들도 함께 검사한다. 어떤 포인터가 "배드"라고 판단되면 당연히 그 포인터가 유효하지 않는 공간을 참조하고 있다는 것을 말한다. 예를 들면 파티션 영역을 넘어서는 곳의 주소를 갖고 있는 경우이다. 이와 같은 경우에 fsck는 다른 해결 방법이 없기 때문에 아이노드나 간접블럭에서 해당 포인터를 단순히 삭제(초기화)한다.
-
디렉터리 검사 : fsck는 파일의 내용을 파악하는 것은 불가능하다. 하지만 디렉터리 내용에 대해서는 파일 시스템이 생성한 구체적이고 서식화된 정보가 있으므로 fsck는 각 디렉터리의 내용에 대해서는 추가적으로 모든 내용이 제대로 저장되어 있는지의 검사를 수행한다. 디렉터리의 첫 항목이 "."과 ".."인지 디렉터리의 각 아이노드가 실제 할당되어 있는지 그리고 전체 계층에서 디렉터리가 두 번 이상 연결된 경우는 없는지를 검사한다.
이렇게 무수히 많은 기능을 하면서 제대로 작동하는 fsck를 만드는 것은 보통일이 아니다. 파일 시스템 전반에 대한 이해가 필요하다. 그리고 fsck는 보다 근본적인 문제점이 존재하는데 그것은 너무 느리다는 것이다. 아주 큰 디스크 볼륨의 경우 디스크 전체에 할당된 모든 블럭들을 찾고 디렉터리 트리 전부를 읽어내는 것은 몇 분에서 수 시간이 걸릴 수도 있다. 디스크의 용량이 커지고 RAID가 대중화되면서 fsck는 실질적으로 사용이 불가능할 정도로 느리다. 이렇게 디스크 용량이 커지면서(그리고 fsck가 느려지면서) 연구자들과 실무자들은 다른 해법을 찾기 시작했다.
해법 2 : 저널링(또는 Wirte-Ahead Logging)
일관성을 담보하는 가장 대중적인 해법은 데이터베이스 관리 시스템에서 차용한 개념 중 하나다. WAL이라고 알려져 있으며 정확히 이러한 문제를 해결하기 위해서 만들어졌다. 파일 시스템에서는 write-ahead logging을 역사적인 이유로 저널링(journaling)이라고 부른다. 현대에 많은 시스템들이 이 개념을 사용하고 있다.
기본 개념은 다음과 같다. 디스크 내용을 갱신할 때, 해당 자료구조를 갱신하기 전에, 먼저 수행하고자 하는 작업을 요약해서 기록해둔다.(디스크의 다른 잘 알려진 위치에) 다양한 기록방법이 존재한다. 새로운 페이지 이미지 전체를 저장하기도 하고, 변경될 부분만 저장하기도 한다. 이렇게 앞으로 할일을 미리 저장해놓는 것을 "write-ahead"라고 하고 "log(로그)"라는 자료 구조에 기록하기 때문에 WAL이라고 부른다.
디스크에 갱신할 값, 즉 갱신 후에 저장될 값과 관련된 내용을 로그에 기록해 놓았기 때문에, 안전하다. 해당 디스크 페이지들을 새 값으로 (덮어쓰기) 갱신하는 과정에서 크래시가 발생하면, 로그를 확인해서 다시 갱신하면 된다. 이를 redo라고 부르기도 한다. 이렇게 하면 크래시가 났을 때 디스크 전체를 다 스캔하지 않아도 어느 부분을 고쳐야 하는지 정확히 알 수 있다.
저널링 파일 시스템에서는 쓰기가 좀 더 복잡해진다. 쓸 내용들에 대한 정보를 미리 로깅을 한 후에 실제 쓰기를 진행하기 때문이다. 쓰기가 약간 느려지지만 그 오버헤드가 의외로 작다. 시스템의 복구 성능을 대폭 개선해주기 때문에 약간의 오버헤드가 있음을 감수하고 대부분의 파일 시스템이 채용하고 있다.
많이 사용되는 저널링 파일 시스템인 Linux ext3 파일 시스템의 저널링 기법을 살펴보자. ext3가 사용하는 디스크 자료 구조는 대부분 Linuw ex2의 그것과 동일하다. 디스크는 블럭 그룹으로 나뉘어 있고 각 블럭 그룹은 아이노드 비트맵과 데이터 비트맵 그리고 아이노드와 데이터 블럭들로 구성되어 있다. 저널 자료 구조가 새로운 핵심 자료 구조이다. 이것은 저장장치의 일부 영역을 차지하며 파일 시스템 포맷 시에 설정한다. 저널이 없는 ext2 파일 시스템은 다음과 같이 구성되어 있다.
저널 영역이 동일한 파일 시스템 파티션에 있다면 저널 영역을 포함하는 ext3파일 시스템은 다음과 같이 구성된다.
저널은 일종의 작업 지시서라고 볼 수 있다. 수행할 동작과 반영될 데이터들로 이루어져 있다. 저널링에서는 두 단계의 업데이트가 존재한다.
1단계 : 업데이트할 내용을 저널로 만들어서 저널링 영역에 저장한다.
2단계 : 업데이트를 실행하고 해당 저널을 삭제한다.
저널링은 일관성을 가지기 때문에 매우 안전하다. 만약 각 단계에서 크래시가 난다면 어떻게 되는지 살펴보자.
1단계에서 크래시 : 저널의 손상
-> 파일 시스템을 수정한 것이 하나도 없으니 괜찮다.
-> 다만 손상된 저널은 삭제된다.
2단계에서 크래시 : 파일시스템 손상
-> 저널이 남아 있으므로 저널에 있는 지시 사항을 처음부터 다시 적용한다.
-> 적용한 저널은 삭제된다.
위 내용을 토대로, 저널링을 사용하면 두 번에 걸친 디스크 접근으로 오버헤드가 생길 것이라 짐작해볼수 있다. 그럼 이 오버헤드는 어떻게 줄여야 할까? 2단계는 즉시 실행할 필요가 없고, 여유가 있을때 수행하면 된다. 2단계에서 파일시스템에 접근할 때 저널들을 먼저 확인해야 하기 때문에 추가적인 오버헤드가 있다. 그렇기 때문에 2단계 작업을 즉시 하는 경우 성능상 이점보다 손해가 더 크다.
'OS' 카테고리의 다른 글
RAID 개념과 종류 그리고 구성방식 (0) | 2021.07.12 |
---|---|
4-5장. 파일 시스템 구현 (0) | 2020.08.02 |
4-4장. 막간 : 파일과 디렉터리 (0) | 2020.07.23 |
4-3장. Redundant Array of Inexpensive Disk (RAID) (0) | 2020.07.23 |
4-2장. 하드 디스크 드라이브 (0) | 2020.05.22 |