Reliability
파일 시스템에서 퍼포먼스와 같이 중요한 요소가 reilability입니다. file system reliability을 한 단어로 표현하자면 data persistency라고 할 수 있습니다. 스토리지 디바이스가 비휘발성(non volatile) 메모리이기 때문입니다.
파일에다 어떤 데이터를 쓰면 그 데이터는 스토리지 디바이스에서 계속 보존되게 됩니다. 언제 읽더라도 일관된 내용을 얻어올 수 있는데 이렇게 consistency를 유지한다는 것은 어려운 일입니다. 오랜 기간 동안 여러 가지 측면에서 조건들이 다 만족돼야 하기 때문입니다.
시간이 길어지면 스토리지 디바이스에서 하드웨어적인 fault가 발생하거나 또는 전원이 꺼지는 문제가 발생할 수 있습니다. 이때 우리가 메인 메모리에 보관하고 있는 스토리지 메타 데이터에 대한 카피본들이 modify 된 후 디스크에 반영되지 않았는데 system crash가 발생한다면 일관성이 깨지게 됩니다.
사용자가 어떤 파일에 데이터 블록을 많이 추가하면 그 내용이 in-memory inode에는 반영이 되겠지만 스토리지에 있는 inode에는 반영이 안됩니다. 스토리지의 inode의 경우 옛날의 인덱스 정보를 가지고 있으니 완전히 파일 시스템의 structure가 무너지는 문제가 생길 수 있습니다.
두 번째 문제는 data integrity 또는 data protection이라고 불리는 문제입니다. 스토리지에 데이터를 저장할 때 노이즈가 발생하거나 전기 전자적 문제로 인해 다른 값이 저장되는 경우 발생합니다. 이 문제는 RAID라고 하는 하드웨어와 소프트웨어가 결합된 특별한 스토리지 디바이스를 통해 해결할 수 있습니다.
Crash inconsistency
스토리지에 서로 연결된 두 데이터를 스토리지 상에 기록한다고 해보겠습니다. 이렇게 연결된 데이터는 inode block들이 할당되었는지 아닌지를 표현하는 bitmap과 inode 정보 자체를 저장하는 데이터를 생각하시면 됩니다. 위 사진의 A를 inode 정보, B를 그 inode가 표현하는 파일의 데이터 콘텐츠라고 해보겠습니다.
두 개는 같은 정보를 표현하니까 항상 같이 저장되어야 합니다. 저장이 안될려면 둘 다 저장이 안 돼야합니다. 스토리지에는 한 번에 한 블록만 저장할 수 있는데 이 때문에 어쩔 수 없이 순차적으로 저장이 됩니다. 그래서 A를 저장한 뒤 B를 저장하거나 B를 저장하고 A를 저장할 때 crash가 발생할 수 있습니다.
정리하면 스토리지에 여러 정보를 저장할 때 crash나 power loss가 발생하면 atomic하게atomic 하게 저장할 수 없게 됩니다. 그리고 이렇게 atomic 하게 저장되지 않은 경우 file system inconsistency가 생길 수 있습니다.
두 번째로 디렉토리에서 파일을 delete 할 때를 생각해 보겠습니다. 디렉터리를 표현하는 파일 콘텐츠가 modity 되고 해당 파일의 inode가 modity 되어 제거되며, 그 파일이 가지고 있는 데이터 블록들이 다 free 되어야 하니 데이터 블록에 대한 bitmap이 업데이트돼야 합니다.
이렇게 3가지 operation이 수행이 되어야 하는데 1~2개만 수행이 되고 crash나 power loss가 발생하게 되면 File system inconsistency가 생깁니다.
3번째의 경우 inode cache 때문에 발생하는 문제입니다. 메인 메모리에서는 항상 inode의 복사본을 유지하는데 파일의 콘텐츠가 변하거나 데이터 블록의 allocation 상태가 변하게 되면 in-memory inode에는 인덱스 값이 다 반영이 되지만 디스크에는 바로 반영이 되지 않습니다. 디스크가 수정되기 전에 crash나 power loss가 발생하면 문제가 생기게 됩니다.
crash consistency를 위해서 unix에서는 file system checker라는 tool이 존재합니다. 최근에는 journaling이라는 기법을 사용하기도 합니다.
File System Checker
파일 시스템 체커를 사용한다는 것은 unix os에서 제공해주는 fsck command를 사용한다는 것입니다. fsck는 주로 시스템 부트를 할 때 수행되는 명령어로 파일 시스템의 상태를 스캐닝하고 crash inconsistency가 있다면 복원하는 동작을 진행합니다.
이렇게 파일 시스템 체커를 사용하여 문제를 해결하는 것을 Lazy approach라고 부릅니다. 사용자들이 file system inconsistency 대해 고려하지 않아도 알아서 뒷정리를 해주기 때문입니다. 하지만 이 fsck도 대부분의 inconsystency를 해결하기는 하지만 해결하지 못하는 부분도 존재합니다.
fsck는 파일과 파일 시스템을 구성하는 모든 data stracture를 전부 traverse 합니다. (모든 inode, data bitmap, inode bitmap 등), 이렇게 탐색하면서 inconsistency가 발생했는지 확인합니다. inconsistency가 발생하면 directory stracture를 다 탐색합니다. 여기서는 delete 과정 중에 아직 delete가 안 되어 있는 파일들이 있는지 확인하고 고칩니다.
이 과정을 거치면 어떤 파일에서 발생했는지 모르는 디렉토리나 디스크 블록들이 남게 되는데 이걸 unix os가 lost와 found라는 디렉터리에 모아둡니다. 유저는 필요에 따라 알아서 복원을 하면 됩니다.
과거에 디스크의 사이즈가 크지 않을 때도 unix os를 리부팅하면 file system checker가 실행되는 시간이 길었습니다. 특히 power loss로 인해 리부팅하게 되면 inconsistency를 회복하는데 굉장히 오래 걸립니다. 디스크가 작았을 때도 오래 걸리는 문제가 존재했기에 더 큰 디스크를 사용하는 현재에는 잘 사용하지 않는 추세입니다.
Journaling
journaling은 file system crash나 power loss가 발생해도 복원이 가능하게 시스템을 만드는 방법을 이야기 합니다. 이를 가능하게 하는 핵심 아이디어는 logging입니다. 이 logging을 write-ahead logging이라고 부릅니다.
파일이 새로운 데이터 콘텐츠를 추가하거나 기록할 때 또는 파일이 delete 될 때와 같이 파일 시스템의 상태가 변화하면 이 상태 변화를 업데이트하기 위해서 어떤 연산들을 해야한다고 설명을 short summary에 작성하는 방법입니다. (업데이트를 하는 것이 아니라 연산 과정만 기록해 놔서 업데이트 값을 얻으려면 연산을 진행해야 합니다.)
이 short summary는 디스크 스토리지의 일정 영역에 저장이 됩니다. 이 log를 많이 쌓아두게 되면 파일 시스템에 있는 파일을 액세스하는데 오버헤드가 발생하게 됩니다. (결괏값이 바로 업데이트된 것이 아니라 log 연산을 통해 결괏값을 얻는 형태)
그래서 crash나 power loss가 생겨 리부팅하게 되면 파일 시스템의 consistency를 확인하는 것이 아니라 journal area에서 log를 읽고 복원하면 됩니다.
저널링 기법을 사용한 대표적인 파일 시스템이 linux os의 ext3, 4 이런 파일 시스템 입니다. linux에서 사용했던 ext2의 경우 저널링을 사용하지 않고 파일 시스템 체커를 사용했습니다. 그래서 스토리지를 포맷팅 하면 위 사진과 같은 모습이 됩니다.
Evolution of Unix File System
1974년 개발된 파일 시스템 FS는 성능 상의 치명적인 한계가 있었습니다. 이걸 UC 버클리 개발자들이 보완하였는데 그게 1984년 개발된 Fast file system 입니다. 이다음에 등장한 파일 시스템이 저널링과 유사한 log structured file system입니다. 마지막으로 과거부터 현재까지 사용되는 ext2, 3 서비스가 1993년에 개발되었습니다.
FS에 대해 자세히 알아보겠습니다. FS의 file system structure는 지금까지 배웠던 파일 시스템과 많이 유사합니다. block base allocation을 담당하고 있으며 indexed file structure를 가집니다. block size는 당시 섹터의 사이즈가 512 byte였기 때문에 512를 기준으로 1~4배의 크기를 갖습니다. 피지컬 블록 넘버는 어레이로 구성하여 array indexed를 사용했습니다.
OS 입장에서는 파일 시스템은 그 스토리지의 볼륨을 포맷팅하는 것입니다. 사용자 입장에서는 파일 시스템이 디렉터리 트리로 보이지만 OS 입장에서는 파일 시스템을 담을 그런 스토리지 볼륨을 포맷팅 한 것이라고 보시면 됩니다.
OS의 오리지널 파일 시스템 포맷은 위 사진과 같습니다. 디스크 블록의 0번은 무조건 boot area로 사용이 되며 그 앞 부분은 super block으로 파일 시스템 전체에 대한 메타 데이터가 들어갑니다. available 한 블록의 개수와 inode로 할당할 수 있는 개수가 몇 개인지의 대한 정보가 들어가는 곳입니다.
superblock은 한 번 작성되면 modify되지 않습니다. 그다음 부분은 파일들마다 필요한 inode를 저장할 수 있는 공간인 inode list가 존재하며 마지막으로 data block이 존재합니다. 이 포맷은 심각한 문제가 있는데 메인 메모리가 아니라 스토리지라는 것을 고려하지 않았기 때문입니다.
메인 메모리는 address만 부여하면 데이터 콘텐츠를 뽑는 random access가 가능하지만 스토리지의 경우 arm을 움직이는 seek를 해야 하기 때문에 이 구조는 seek의 움직임을 전혀 반영하지 못한 구조입니다.
먼저 superblock에서 문제가 발생합니다. superblock은 파일 시스템에 존재하는 블록의 개수, inode, free block, free inode, 그다음 이런 free block과 inode를 링크드 리스트로 만든 자료 구조를 가지고 있습니다. 이 정보 중 하나가 어긋난다면 free block list에 대한 포인터를 상실해 crash가 발생하게 됩니다.
하지만 superblock에서는 이런 문제에 대비하여 복사본을 가지고 있어야 하는데 하나도 없습니다. 그래서 superblock에 문제가 생기면 전체 파일 시스템의 데이터에 액세스 할 수 없다는 치명적인 단점을 가지고 있습니다.
두 번째 문제는 성능상의 문제로 seek optimization이 고려되지 않았다는 점입니다. seek를 줄일려면 파일의 콘텐츠와 inode가 인접한 블록에 있어야 합니다. 하지만 이 파일 시스템의 구조는 inode는 inode 끼리 멀리 모여있고 디스크의 데이터 블록은 디스크 데이터 블록끼리 멀리 모여있기 때문에 많은 seek가 발생합니다.
이렇게 단점들이 존재하기는 하지만 초창기 unix 파일 시스템은 작은 OS로 빠르고 간단하게 만든다는 목적을 가지고 있었기에 이런 파일 시스템 구조를 가지게 된 것입니다.
'CS > Operating System' 카테고리의 다른 글
30. Evolution of File system (1) | 2024.02.05 |
---|---|
28. Free Block Management (0) | 2024.02.04 |
27. File Structures (0) | 2024.02.04 |
26. File System (0) | 2024.02.03 |
25. Files and Directories (0) | 2024.02.03 |