파일 시스템 구현을 위한 기본 기능과 컨셉 정의

Basic Functionalities and Concepts for File System Implementation

 

파일 시스템 개발을 위해서 우리는 NT 커널의 기본 구조와 파일 시스템이 긴밀히 관여 해야 하는 정책, 실행부 자원(캐시 매니저, 가상 메모리 매니저, I/O 매니저)등을 알아 보았다. 실제 파일 시스템 그 자체를 설계하고 구현 하는 것은 이러한 운영체제에 의존성을 가진 것들과 큰 관여를 하지 않을 수 있지만, 파일 시스템과 관련된 NT 커널의 실행부 자원과 정책들을 이해해야 하는 것은 Windows의 기존 파일 시스템들처럼 NT 커널 내에서 적절히 상호 커뮤니케이션 하면서 신뢰성과 호환성 등을 고려 하기 동작하기 위해서이며, NT 아래서는 베니어(Veneer) 레이어가 하는 완충 작용과 프로토콜을 따리 주지 않으면 제작하는 파일 시스템이 정상적으로 동작 할 수 없기 때문이다.

 

본 칼럼은 파일 시스템 구현 섹션의 첫 칼럼으로써 Windows NT환경에서 파일 시스템의 설계 정책과 베니어 레이어의 기능을 정의하고, 이를 위한 자료구조의 일부를 알아 볼 것이다. 또한 운영체제에게 현재 파일 시스템의 존재 여부를 보고하기 위하여 레지스트리를 어떤 식으로 사용 해야 하는 지도 알아 볼 것이다.

 

정명수 |

필자는 지난 3년간 삼성전자에서 플래시 메모리와 관련된 연구와 임베디드 소프트웨어, 커널 드라이버 등을 개발 했었다. 현재는 조지아 공대(Georgia Institute of Technology) 컴퓨팅 칼리지에 재학 중이다. 글쓰기를 매우 좋아하며 학부시절에는 객체 지향 패러다임을 통하여 해석하는 프로그래밍 언어론에 관심이 있었으나 실무과정을 거치면서 컴퓨터 아키텍처로 관심사가 옮겨졌다. 최근에 관심 있는 분야는 운영체제, 파일시스템, 실시간 스케줄링 등이다.

 

이 칼럼을 읽고 있는 대부분의 개발자나 설계자들은 파일 시스템 전체를 구현 해야 하는 경우에 있지는 않을 것이다. 컴퓨터 공학과 과학 쪽에서 이제는 대부분의 문제나 이슈들이 수렴되고 거의 안정화 단계에 들어가는 분야가 크게 두 가지가 있는데, 하나는 캐시 관련 문제이고 다른 하나는 파일 시스템 관련 문제이다. 이런 이유 때문에 상업적으로 사용할 목적으로 독립적 파일 시스템을 구현해야 하는 개발자는 매우 드물다. 그러나 많은 사람들이 윈도우 NT 커널에 파일 시스템이 어떤 식으로 구성되고 동작하는 지 그리고 파일 시스템의 설계, 구조, 동작 알고리즘과 같은 것들을 궁금해 한다. 나아가 많은 사람들이 파일 시스템과 같은 형태의 기능을 가지는 소프트웨어를 구현 해야 하거나 또는 파일 시스템과 긴밀히 연관되는 모듈을 작성 해야 할 수 있다. 긴밀히 연관 되는 모듈은 어떤 것들이 있을까? 예를 들면 독자들이 어플리케이션 레벨에서 의사 파일 시스템(Pseudo file system)형태로 소스 코드를 관리 해주는 프로젝트를 개발 할 수 도 있고, 파일 시스템 드라이버에 전달되는 입출력을 가로채어 이를 가공 한 뒤 파일 시스템에 다시 넘겨주거나 블록 디바이스(Block device)에게 전달 해주는 필터 드라이버를 개발 해야 할 수 있고, 현재 Window NT관련된 파일 시스템들(NTFS, FASTFAT, FAT32등)들의 일부 기능만 수정해서 사용 해야 할 수도 있다. 사실 어떤 이유에서 파일 시스템과 관련된 모듈을 개발하던 간에 파일 시스템의 설계 및 구현에 대한 충분한 이해가 필요한 것은 당연하다.

 

우리는 파일 실제 파일 시스템 개발을 위한 본 칼럼 섹션(File System Driver Implementation)을 위해서 아래와 같은 NT 커널의 실행부 자원, 매니저들을 알아 보았다.

  1. NT 주요 아키텍처와 파일 시스템(NT Core Architecture and File system)
  2. 가상 메모리 매니저(Virtual Memory Manager)
  3. NT 캐시 매니저(NT Cache Manager)
  4. NT I/O 매니저(NT I/O Manager)
  5. 가상 주소 번역(Virtual Address Translation)

 

본 컬럼 섹션에서는 위에서 언급된 NT 컴포넌트(Component)들을 기반으로 하여 Windows NT 환경에서 파일 시스템 구현 자체에 초점을 맞출 것이다. 따라서 구현을 이해하기 전에 위의 컬럼들과 운영체제의 기존 이슈들을 이해하지 못한다면 반드시 이들을 읽어보는 것을 권한다. (운영체제에 대한 기본지식들은 필자의 블로그에서 확인 할 수 있다.)

 

앞으로 진행되는 장기간 진행되는 파일 시스템 구현 섹션은 파일 시스템에 대한 개발자들의 이해를 최대한 돕기 위해서 우선적으로 파일시스템 자료구조를 확인하고 파일 시스템이 Window NT 커널 환경에서 제대로 구현 되기 위한 디스패치루틴(Dispatch routine)들을 알아 볼 것이다. 디스패치 루틴과 자료구조들은 실제 Window NT의 내부구조를 이해하지 못한 독자라면 이해에 어려움을 겪기 쉬우므로 이해를 돕기 위해 각 디스패치 루틴과 관련된 파일 시스템 자체의 기능들을 기술하고 필요하다면 의사코드, 다이어그램 등으로 설명을 보충 하도록 하겠다.

 

파일 시스템 설계

제대로 된 설계가 없이 주먹구구식으로 작성된 파일 시스템은 이 세상에 없다. 제대로 된 설계를 위해서 우리가 파악 해야 하는 것은 우리가 작성해야 할 파일 시스템의 목적과 그 목적을 달성하기 위한 상세 기능 설계이다. 이러한 설계방식은 일반적인 개발방법론에 나오는 설계 방식과 유사하게 보이지만 실제는 그렇지 않다. 왜냐하면 파일 시스템은 다른 프로그램들과 달리 기능성 요구사항 명세(Functional Requirement)보다 비기능성 요구사항(Non-functional Requirement)가 많기 때문이다. 다시 말해 대부분의 파일 시스템은 기능성 요구사항은 읽기, 쓰기, 초기화, 생성 정도로 정해져 있기 때문에 비기능적 요구사항이 파일 시스템이 골이 되고 이를 얻기 위한 올바른 설계가 수반되어야 한다. 예를 들어 빠르고 간단한 파일 시스템을 개발 했다고 하면 이 파일 시스템의 개발 골은 신뢰성, 편리한 유지보수, 데이터 저장의 편리성등의 비기능성 요구사항이 될 수 있다. 따라서 이 파일 시스템은 현존 하는 소프트웨어나 하드웨어의 비정상적 동작에 의해 파일 시스템의 일관성(Consistency)가 깨질 수도 있으며 데이터 보안 같은 좀 더 복잡한 형태의 파일 시스템의 특징을 반영하지 않는다. 이러한 형태의 파일 시스템으로는 과거 FAT과 같은 형태의 파일 시스템을 들 수 있다. FAT과 NTFS는 모두 기능성 요구사항은 다르지만 FAT은 NTFS보다 불안정하긴 하지만 훨씬 간단한 형태의 메터정보(Meta Information) 활용과 구조변형 및 유지보수를 쉽게 할 수 있다.

 

NTFS의 예를 들어보자. NTFS는 Windows NT기반에 맞도록 구현된 로그 기반의 파일 시스템 이다.(Log-based file system, 좀 더 자세한 이해를 원하는 독자는 Mendel Rosenblum와 John K. Ousterhout의 논문 The Design and Implementation of a Log-Structured File System를 반드시 읽어보도록 권한다. 이 논문은 모든 로그기반의 파일 시스템의 근원이 되는 논문으로 이를 이해하는데 큰 도움이 될 것이라고 필자는 확신한다) 이 파일 시스템은 앞서 언급된 FAT과 같은 형태의 빠르고 간단한 형태의 파일 시스템과 달리 기본 목표가 시스템 실패로부터 빠른 복원과 데이터 일관성(Consistency)를 위해서 만들어졌으며, 데이터 압축이나 바이트 범위의 잠금 기능을 제공한다. 이런 로그 기반의 파일 시스템보다 복잡한 형태가 분산 파일 시스템이다. 앤드류 파일 시스템(Andrew File System)과 같은 형태의 DPS(Distribute File System)는 파일 시스템 구현이 훨씬 복잡한 형태의 좋은 예가 될 수 있다. 이 파일 시스템은 NTFS와 같은 로컬 파일 시스템의 기능과 함께 원거리 서비스를 위한 클라이언트 서버 컴포넌트를 조합하여 하나의 네임스페이스(Namespace)로 이를 접근 할 수 있도록 해준다. 엔드류 파일 시스템 이외에 마이크로 소프트의 오브젝트 파일 시스템(Object File System)또한 복잡한 형태의 파일 시스템 목표와 디자인 설계를 가지고 있다.

 

파일 시스템 코드

앞으로 우리가 컬럼에서 실습하여 작성해볼 간단한 파일 시스템(이후 SFS또는 Simple File System으로 이를 호칭)의 골을 설계해보자. 우리가 작성할 파일 시스템은 특정한 하드웨어 없이 Windows NT 커널의 컴포넌트와 파일 시스템 드라이버 사이에서 상호작용 정도를 조작하는 형태의 간단한 파일 시스템이다. 따라서 이 칼럼에서는 파일 시스템 그 자체(예를 들면 메타 정보관리 및 블록 디바이스 특성을 고려한 설계 및 구현)에 대한 구현은 가급적 생략하여 일반적인 형태로 윈도우 위의 파일 시스템 개발에 도움이 되고자 한다. 모든 파일 시스템은 정의된 시스템 서비스 루틴을 사용하여 데이터 관리를 요하는 사용자들의 요구를 만족 시켜주기 때문에 파일 시스템 구현은 반드시 현재 존재 하는 운영체제와 아주 밀접한 관련이 있다. 이 외에도 앞서 언급된 메타 데이터의 예처럼 파일 시스템은 사용자 데이터를 저장하는 것을 관리하는 자료구조를 관리하는데 이 자료구조는 일부를 제외하고 기본적으로 자신들이 관리하는 블록 디바이스 상에 존재하기 때문에 이들을 관리하는 것도 파일 시스템의 특징 중 하나이다.

 

앞서 언급한 여러 가지 기능/비기능성 요구사항을 만족 시키고 모듈들을 분리하여 사용하기 위해서 가장 많이 사용 되는 아키텍처 방식이 레이어드(Layered)아키텍처이다. 이전 칼럼에서 레이어드 아키텍처의 특징과 간략한 소개는 한적이 있으므로 파일 시스템을 한 레이어로 봤을 때 이 안에서 다시 각 기능들을 통상적으로 어떻게 나누어 설계하는지 살펴보자.

 

<그림 1 파일 시스템 드라이버의 일반적 레이아웃(layout)>

 

Window NT커널 내에서 동작하기 위해서는 NT 커널의 시스템 인터페이스와 함께 파일 시스템이 잘 조화를 이루도록 설계되어야 한다. 요구사항 분석에서부터 설계된 파일 시스템, 그 자체로만도 구현이 복잡하고 유지보수가 어려워지는 경향이 있는데 만약 이러한 파일 시스템을 그대로 NT 커널에 붙이려고 한다면 기능 구현 및 유지보수가 훨씬 어려워진다. NT 커널의 시스템 인터페이스들은 단순히 API수준을 사용하는 것과 달리 운영체제의 행동과 특성에 따라 API의 사용순서, 자료구조 운영 등이 복잡하므로 이러한 특성과 파일 시스템 자체 기능을 하는 코어 사이에서 이를 완충 해주는 작업을 하는 레이어가 필요한데 이런 작업을 베니어(Veneer)가 처리한다. 베니어는 상위레벨의 시스템 인터페이스를 변환하여 실제 파일 시스템에게 기능/비기능적 요구를 지시 할 수 있게 하는 기능을 한다. 코어는 이런 베니어가 변경하여 내려주는 파일 시스템으로서의 요구사항을 만족 시키고 블록 디바이스에 존재하는 실제 데이터들을 관리하는 역할을 한다. 마지막으로 블록 디바이스 인터페이스 레이어는 하위 레벨의 기존 디스크나 네트워크 드라이버와의 조율을 해주는 역할을 한다. 중요한 것은 베니어 레이어의 역할이 NT 커널 내의 파일 시스템에서는 매우 중요하다는 것이다. 드라이버를 한번이라도 개발을 해본 사람은 반드시 겪었을 시스템 패닉이나, 운영체제의 오동작들은 파일 시스템 내의 기능/비기능의 구현보다도(실제 코어들은 시뮬레이션을 통해서 충분히 검증 되어 올라오는 경우가 더 많다) NT 커널을 잘 이해하지 못하고 작성하여 베니어의 기능을 충실히 못했을 가능성이 더 많이 존재 한다.

 

NT계열을 파일 시스템 구현을 포함하여 대부분의 파일 시스템들은 아래와 같은 구조로 위치한다.

 

<그림 2 NT커널에서 파일 시스템의 위치>

 

그림 2에서 표시된 정보의 흐름, 1,2,3 은 파일 시스템의 기본 기능이라기보다 베니어의 기능으로 볼 수 있다. 우리는 Windows NT 커널에서 코어와 관계 없이 현재 파일 시스템 드라이버의 기능을 수행할 수 있는 베니어 개발에 초점을 맞출 것이다. 따라서 블록 디바이스로부터의 데이터를 실제 인출, 배열, 조작등에 대한 부분은 언급 하지 않는다. 이제까지 칼럼에서 누차 언급 되었듯이, NT I/O 매니저,캐시 매니저, 가상 메모리 매니저들과 함께 상호 작용을 이해하고 머릿속에 그려보는 것은 아주 중요하다. 이것은 우리가 상업적인 파일 시스템이나 필터, 플러그형태의 소프트웨어를 Windows NT 커널아래 스스로 자신의 기능을 가지는 네이티브(Native) 파일 시스템의 기본 조건이다.

 

 

심플 파일 시스템 드라이버(Simple File System, SFS)

우리가 앞으로 구현할 SFS는 앞서 언급 된 것처럼 베니어 구현이슈를 주로 다룬다. 파일 시스템을 개발, 설계하는데 있어서는 아주 많은 설계 이해관계의 충돌, 그리고 비기능적 요구사항의 선택 등의 절차들이 필요하다. 예를 들어서 보안성을 높이기 위해서는 보안모듈에 초점을 맞춘 파일 시스템과 메타 데이터들의 배열구조를 생각 해봐야 할 것이고, 병렬성을 최대화 하는 파일 시스템을 원한다면 각 데이터들의 동기화, 그리고 블록 디바이스의 병렬처리 등에 큰 관심을 쏟아야 할 것이다. 반대로 만약 우리가 현업에서 적은 인력과 빠른 솔루션 확보를 강요당한다면 병렬성을 최대한 줄이는 설계방식을 선택 해야 할 것이다. SFS는 이러한 설계 이슈, 그리고 상업적 파일 시스템으로서의 제공 기능에 대한 설계를 다루지 않으므로 이 SFS가 실제로 여러분이 현업에서 만들 사용 파일 시스템 전체를 구현해주지는 않지만, 여러분이 Windows NT 커널아래에서 안정적이고 정상적으로 동작하는 파일 시스템을 구현하는데 있어서 뼈대를 제공할 것이다.

 

베니어로서의 SFS구현 시 메모리 관리는 매우 중요한 문제이다. 일반적으로 다른 인터페이스를 조율하는 래핑(Wrapping)모듈은 서로 다른 파라미터를 변경하고 조율하기 위해 메모리 할당 및 복사가 많이 이루어진다. 우리가 구현할 SFS를 비롯하여 필터 드라이버등과 같은 모든 커널 드라이버는 메모리 할당문제를 염두에 두고 있어야 한다. 우리는 SFS뿐만 아니라 모든 드라이버 개발에 있어서 효율적인 메모리 관리가 중요한 목표 중에 하나라는 것을 잊으면 안 된다. 만약 독자가 구현하는 파일 시스템 드라이버가 비페이징 영역의 메모리를 요구한다면 반드시 해당 드라이버가 동작 시에 메모리 사용량을 측정하여 이를 최소화 할 수 있도록 노력해야 한다. 아무리 우리가 메모리를 비페이징 영역이나 페이징 영역에 잘 분리하여 메모리를 할당 하였다고 하여도 페이징 자체가 매우 비싼 동작이기 때문에 최소화 자체에 많은 신경을 써야 한다. 빈번한 페이지 폴트나 TLB 폴트는 우리가 작성하는 파일 시스템 드라이버뿐만 아니라 전체 파일 시스템에서 심각한 성능 저하를 보이게 한다.

 

레지스터 사용

대부분의 파일 시스템 구현에는 실제 코드 이외에도 많은 수의 키와 Windows NT 레지스트리에 값 개체를 생성 해주어야 한다. 우리는 SFS 파일 시스템 드라이버를 위해서 로컬머신쪽 레지스트리에 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SFS라는 이름으로 키를 등록한다.

 

값 개체/키

유형

비고

ErrorControl

REG_DWORD

0x1

만약 드라이버 로드를 실패한다면 이에 대한 정보를 표시해줄 메시지박스(Messagebox)와 로그정보를 남기는 것에 대한 여부를 표시하고 드라이버가 자동으로 다시 로드를 시도한다면 최기화를 계속함..

Group

REG_SZ

"File System"

Group 값 개체의 "File System"은 지금 등록하는 드라이버가 파일 시스템 그룹에 속함을 나타냄. 예를 들어 해당 드라이버가 네트워크 리디렉터를 개발한다면 여기 값을 "File system"이 아닌 "Network Provider"로 변경해야함.

ImagePath

REG_EXPEND_SZ

"%System-Root%\System32\drivers\SFS.sys"

드라이버 이미지의 경로 완전한 경로 이름을 나타낸다. %안은 환경변수로서 각 시스템마다 정해진 시스템 루트의 경로로 치환된다.

Start

RED_DWORD

0x2 또는 0x3

0x2는 자동으로 드라이버가 시작되는 것을 의미하며 0x3은 수동으로 드라이버를 시작시키겠다는 설정이다.

Type

REG_DWORD

0x2

파일 시스템 드라이버임을 기술함.

Parameters

-

-

이 키에는 드라이버가 필요로 하는 설정 파라미터들이 들어 있다. 개발자들은 이 키아래에 드라이버에서 필요로 하는 자신의 설정 값을 지정 할 수 있다.

<표 1 Simple File System, SFS관련 레지스터 설정>

 

SFS와 관련된 레지스트리 설정을 마쳤다면 이벤트로드에 대한 값을 설정한다.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ServiceS\EventLog\System\SFS 에 등록하는 이 키는 NT 이벤트 로그(Event Log)를 이용한 SFS를 통하여 이벤트 로그를 보여주는 어떠한 어플리케이션이라도 메시지를 확인할 수 있도록 해준다.

 

값 개체

유형

비고

EventMessageFile

REG_EXPAND_SZ

"%System-Root%\system32\sfsevent.dll

NT 이벤트 로그안에 SFS가 남겨놓은 이벤트 및 로그 기술내용을 위한 전체 경로

TypesSupported

REG_DWORD

0x7

에러 메시지, 유의점과 같은 것들을 모두 기록에 남기도록 해줌.

<표 2 이벤트 로그 관련 레지스터 설정>

 

SFS 드라이버 자체에는 유용하지 않을 지 모르지만 SFS의 제작자, 드라이버의 부가설명이나 기타 정보들을 남겨 둘 수 있는 키도 있는데 이 키는 HKEY_LOCALJVIAMACHINE\SOFTWARE\SFS 위치에 기록이 가능하다.

값 개체

유형

비고

VendorName

REG_SZ

"I MASO"

개발자의 조직을 구분할 수 있는 어떤 문자열이라도 관계 없다.

CurrentVersion

-

-

현재 드라이버의 버전 정보들을 기술 할 수 있다.

< 표 3 기타 파일 시스템 정보 관련 레지스터 설정>

 

소프트웨어의 버전을 관리할 때는 하나의 정보만으로 유용하지 않을 경우가 있는데 이러한 경우를 위해서 CurrentVersion에는 VirsionMajor, VersionMinor, VersionBuild, InstallDate와 같은 서브 키를 이용해서 이를 기록 할 수 있다.

 

자료구조

어떤 파일 시스템 드라이버를 설계하던 간에 그 파일 시스템이 사용할 자료구조를 정의 해야 하는데 일반적으로 이러한 자료구조는 어디에 상주하는가에 따라 크게 두 가지로 분류 할 수 있다. 첫째는 디스크상에 자료구조를 두는 것으로 파일 시스템드라이버가 이 자료구조를 필요로 하거나 갱신 해야 할 때마다 이를 디스크상에서 읽어야 하며 이에 대한 주소를 모두 기억하고 있어야 한다. 이를 온-디스크(On-disk) 자료구조라고 부르며 이와 반대로 메모리상에 올려두고 빠른 엑세스를 하도록 하는 자료구조를 인-메모리(In-memory) 자료구조라고 부른다. 만약 파일 시스템 드라이버 개발자중에서 특정 드라이버의 자료구조의 유형과 커널 콤포넌트를 이해야 하는 경우라면 (사실 대부분이 그렇지만) 이 자료구조가 인-메모리인지 온-디스크인지에 따라 이를 다루는 방법이 상이하므로 이에 대한 이해가 필요하다.

 

사실 이상적인 상황이라면 운영체제를 설계 함에 있어서 특정 파일 시스템과는 독립적으로 온-디스크 자료구조와 이에 대한 주소 레이아웃을 가도록 설계 되어야 한다. 온-디스크 자료구조뿐만 아니라 인-메모리 자료구조 또한 어떤 파일 시스템 드라이버든 간에 해당 운영체제 위해서 해당 파일 시스템 드라이버가 제공하고자 하는 기능을 쉽게 구현하기 위해서 어떤 의존성 없이 제공되어야 하지만, Windows NT를 포함하여 비상업적 운영체제는 물론이고 상업적으로 잘 설계된 대부분의 운영체제 또한 이러한 이상적인 환경을 지원 하지 못한다. 다만, Windows NT의 경우, 커널이 파일 시스템에 관련된 자료구조에 특별한 관여를 하지 않는 수준의 설계를 가지고 있다. 따라서 파일 에 대한 정보들을 기대하고 있는 사용자의 경우, 만약 여러분이 제작한 파일 시스템 드라이버가 디스크 위에 파일 이름, 접근 시간, 마지막 쓰여진 시간 등의 특성을 관리하지 않는다면 해당 사용자는 매우 혼란스러울 수 밖에 없다.

 

Windows NT와 관련된 마이크로 소프트사의 파일 시스템들은 각 파일 시스템마다 각기 다른 특징들을 가지고 있다. 예를 들면 FASTFAT의 경우 접근 제어 리스트(Access Control List)에 저장되는 파일의 보안 정보등을 전혀 제공하고 있지 않기 때문에 파일에 대한 다중 링크, 파일 압축 또는 시스템 실패 시 복원을 위한 파일 복원 정보들을 전혀 가지고 있지 않다. 반면 NTFS의 구현에서는 앞서 언급된 모든 기능들이 전부 구현 되어 제공되고 있으며 이에 대한 정보들을 모두 온-디스크 자료구조에 담아두고 있다. 이러한 온-디스크 자료구조는 FAT 계열의 파일 시스템과 완전히 다른 특성을 보여준다.

 

앞선 섹션에서 언급 되었듯이 우리가 파일 시스템 드라이버를 구현하고자 하는 목적은 Windows NT 기반 아래서 특정 파일 시스템의 기능과 관계 없이 베니어와 같은 형태의 파일 시스템 드라이버를 논의 하고자 하는 것이므로 특정 파일 시스템의 제작에 대한 온-디스크 자료구조에 대해서는 더 이상 언급을 하지 않을 것이다. 다만, 파일 시스템 개발자가 알아 두어야 할 것은 파일 시스템 개발에서 온-디스크 자료구조를 사용 하기 위해서는 해당 자료구조를 블록 디바이스로 입출력 시키기 위해서는 다양한 자료구조를 단지 기능에 의한 추상화수준으로 자료구조를 설계 하면 안되고 블록디바이스의 입출력 사이즈, 접근성 등을 고려하여 레이아웃을 잡아야 한다는 것이다. 다시 돌아 와서, 우리의 목적에 맞도록 파일 시스템 드라이버가 반드시 정보를 유지해야 하고 그것을 관리하기 위한 자료구조를 구현하기 위해 우리는 인-메모리 자료구조에 좀 더 초점을 맞추도록 한다.

 

비록 Windows NT에서 파일 시스템 드라이버를 설계, 제작하기 위해 문서화 해놓거나 권장하고 있는 자료구조들은 없지만 우리는 파일 시스템 드라이버를 구현 하기 위해서 FCB로 불리는 파일 컨트롤 블록(File Control Block)과 CCB로 불리는 컨텍스트 컨트롤 블록(Context Control Block) 자료 구조 두 가지에 대해서는 반드시 고려 해주어야 한다. FCB는 온-디스크 자료구조를 시스템 메모리 상에 올려 놓은 것을 참조 할 수 있는 유일한 자료구조이다. 예를 들면 디렉토리, 파일, 자료구조의 볼륨, 그리고 우리가 작성할 파일 시스템 드라이버가 관리하는 자료구조인 오브젝트들은 FCB에 의해서 참조된다. 만약 독자가 UNIX 구현에 대한 배경 지식이 있다면 메모리에 상주하면서 파일을 표현하는 자료구조로서 vnode가 Windows NT의 파일 컨트롤 블록, FCB와 유사하다는 것을 알 수 있을 것이다. vnode와 FCB 두 자료 구조 모두 디스크상에 파일을 관리하고 참조 할 수 있도록 정보를 담고 있는 메모리상에 상주한다.

 

반면 컨텍스트 컨트롤 블록, CCB는 열려 있는 파일의 온-디스크 오브젝트들을 관리하고 참조 할 수 있는 정보들을 가지고 있기 위해서 파일 시스템에 의해서 생성되고 조작되는 자료구조이다. 예를 들어서 사용자가 특정 파일에 대해서 열기에 대한 동작을 수행하면 운영체제는 해당 파일에 대해서 열기를 시도하고 만약 성공하게 되면 해당 파일에 대한 제어권을 사용자에게 넘겨준다. 해당 제어권(핸들, handle이라고도 불림)을 관리하기 위해 Windows NT의 파일 시스템들은 컨텍스트 컨트롤 블록을 생성하고 관리 해야 한다. 컨텍스트 컨트롤 블록은 각 파일 마다 하나 각각 존재하는 오브젝트 핸들로 파일 시스템에 의해서 유일하게 관리 되기 때문에 파일 시스템 드라이버 개발자(특히 특정 레거시 파일 시스템을 제작하는 개발자)들은 컨텍스트 컨트롤 블록을 어떻게 관리 할 것인지에 신경을 써야 한다.

 

온-디스크와 인-메모리 자료구조에 대해서.

온-디스크 자료구조와 인-메모리 자료구조가 다루는 정보들은 매우 유사하다. 따라서 어떤 파일 시스템에서는 두 자료구조를 동일 한 것을 사용 하는 경우도 있다. 처음 이런 자료구조들을 겪으면 매우 혼란스러울 수 있는데(개발자의 위치나 역할에 따라) 둘 간의 차이를 파악 할 때는 단순히 개발 순간에 자료구조가 위치하는 것을 보는 것 보다 실제 그 자료구조가 저장 되어야 하는 위치를 보는 것이 좋다. 예를 들어 인-메모리에 있더라도 저널링을 위해서 반드시 블록 디바이스에 저장 해야 한다면 이런 경우는 온-디스크 자료구조로 이해하고 필요한 경우 메모리에 블록 디바이스로부터 읽어서 이를 메모리에 상주 시키는 것으로 이해하는 것이 편하다.

 

파일 시스템의 베니어 레이어에서는 인-메모리를 일일이 블록 디바이스로 내려가며 저장해야 할 경우가 크지 않다. 대부분 커널에서 이를 관리하게 되기 때문에 대부분 인-메모리로 보면 된다. 경우에 따라(다시 말해 작성하는 파일 시스템에 따라) NT 커널이나 드라이버간의 관계와 연관 없이 특정 파일 시스템의 신뢰성, 일관성, 일치성등을 위해서 온-디스크 자료구조를 직접 설계하고 저장해야 하는 경우가 있는데 이때는 이를 구분하여 적절한 시간에 반드시 블록 디바이스로 저장 해주는 작업을 병행 해야 한다.

 

 

메모리에 상주시켜야 하는 파일 관리 참조에 관련된 일련 정보

NT I/O 매니저 컬럼에서 언급 했듯이 I/O 매니저가 열기 동작에 대하여 적절한 파일 오브젝트를 전달 해주는데 이를 통하여 파일 컨트롤 블록, 컨텍스트 컨트롤 블록, 디바이스 오브젝트 등의 유용한 자료구조에 접근 할 수 있다. 파일 시스템 개발자는 지금부터 소개 되는 구조체를 생성하거나 또는 초기화, 플래그 변경에 대해서 다른 어떤 것들보다 큰 책임이 있다. 따라서 각 구조체간의 관계 등을 숙지 해 놓는 것이 좋다. 큰 그림이 그려지면 이 자료구조들을 어떻게 사용 할 것인지, 또 프로토콜을 맞추기 위해 어떤 흐름을 지켜줘야 하는지, 템플릿에 가까운 코드들을 일일이 작성 해주어야 하는 지 훨씬 이해하기 쉬울 것이다.

 

물리 디바이스 오브젝트(Physical Device Object)

자료구조 스택에서 가장 아래 존재하는 디바이스 오브젝트부터 살펴 보자. 물리 디바이스 오브젝트는 디바이스 오브젝트는 크게 아래와 같은 DeviceType들의 조합으로 이루어 진다.

  1. FILE_DEVICE_DISK
  2. FILE_DEVICE_VIRTUAL_DISK
  3. FILE_DEVICE_CD_ROM

 

이 자료 구조는 IoCreateDevice()를 통해서 생성되는데 일반적으로 물리 또는 가상 디스크 오브젝트를 관리 할 때 사용한다. 생성시간에 VPB라고 불리는 볼륨 파라미터 블록(Volume Parameter Block) 자료구조가 생성되고 NT I/O 매니저에 의해 미디어 타입이 할당된다. VPB 자료 구조 초기화 시에 VPB_MOUNTED 플래그는 빈 상태의 값을 가지게 되는데 이는 해당 물리 디바이스에 논리 볼륨이 할당 되지 않았다는 것을 의미한다.

 

<그림 3 파일 시스템 관련 자료구조간의 관계도>

 

이는 이후 파일 시스템 구현 시에 (필요하다면) 해당 물리 디바이스에 논리 볼륨을 마운트하기 전에 이를 검증하기 위해 사용되기도 한다. 다시 말해 마운트 할 것인 아닌지를 이 플래그를 통해 확인 할 수 있다는 것을 의미하기도 하고 나중에 다음 논리 볼륨 디바이스 오브젝트를 엑세스 할 수 있게도 해준다.

 

논리 볼륨 디바이스 오브젝트(Logical Volume Device Object)

그냥 볼륨 디바이스 오브젝트라고 불리기도 하는데 이는 마운트된 논리 볼륨 정보의 인스턴스라고 보면 된다. 볼륨 디바이스 오브젝트는 파일 시스템 구현 시에 개발자에게 의해서 생성된다. 마운트라는 것은 블록 디바이스에 있는 자료를 사용자가 접근하기 전에 일반적으로 대부분의 운영체제에서 행해지는 동작 중 하나이다. 개발자 입장에서 본다면 볼륨을 마운트하는 동작은 파일 시스템 드라이버에게 해당 블록 디바이스에 계속되는 접근을 위한 준비의 기회를 주는 것이라고 보면 된다.

 

따라서 파일 시스템이 볼륨을 마운트 하는 대부분의 동작은 파일 시스템 그 자체 또는 특성에 따라 모두 달라 질 수 있으며 이 기간 동안에는 운영체제의 간섭을 최소화 하고 있다. 일반적으로 이 시간에 가장 먼저 하는 수행 하는 것이 온-디스크 구조체를 확인 하여 해당 블록 디바이스 할당된 다른 파일 시스템이 있는지를 확인 하는 과정을 거친다. 이 과정을 거치고 나면 파일 시스템은 볼륨 크기, 루트 디렉토리의 위치, 여유 블록의 사상 정보, 할당된 클러스터의 사상정보 같은 기본이 되는 볼륨 정보를 읽고 볼륨 엑세스를 하기 위해 필요한 자료구조들을 인-메모리로 보관하기 위해 필요한 메모리 사이즈를 확보한다. 논리 볼륨 마운팅의 일부로서 대부분의 운영체제시스템에서 정의된 자료구조를 생성, 초기화 시키기를 권장하고 I/O 메니저 구조체와 인-메모리 구조체로서의 존재하는 마운트 볼륨의 정보들의 관계를 정의하도록 배려해준다. 따라서 파일 시스템 설계자들은 반드시 파일 시스템 구현을 할 플랫폼의 운영체제 요구사항을 숙지 해야 함은 물론이고 올바른 자료구조를 적시에 할당 해주어야 한다.

 

Windows NT에서는 I/O 매니저가 생성된 논리 볼륨과 함께 물리 블록 디바이스 또는 미디어(Media)의 정보를 가지고 있는 디바이스 오브젝트를 요구하는데 해당 디바이스 오브젝트가 정상적으로 초기화 되었는지 등을 VPB를 통해서 알 수 있다. 다시 말해 각 볼륨 디바이스 오브젝트는 논리적으로 물리 디바이스 오브젝트를 가지고 있는 VPB 자료구조를 사용하여 물리 디바이스 오브젝트와 연관 시킨다. 앞서 언급 되었듯이 이 관계는 해당 물리 디바이스 오브젝트에 대해 I/O 매니저가 처음 열기 또는 생성 동작을 할당 받는 볼륨 마운트 시점에서 생성된다.

 

이러한 관계를 코드상에서 명확히 설정 해주는 것과 이에 대한 논리적 볼륨 디바이스 오브젝트는 파일 시스템 개발자에게는 매우 중요한 과정이다. 왜냐하면 우리가 만드는 파일 시스템 드라이버에 의해서 이 동작들이 수행 되어야 하며 사용자의 생성, 열기에 대한 디바이스 오브젝트와 타겟 드라이버를 결정하는 것은 앞서 언급한 과정을 통해 I/O 매니저에 의해 관리 되기 때문이다.

 

이 과정이 중요한 만큼 이에 대한 절차를 그림 4로 정리 해보았다.

 

<그림 4 마운트 초기 작업 의사 결정도>

 

마운트의 특정 작업이 끝나고 나면 VPB내에 DeviceObject필드를 포인터로 사용하여 논리 볼륨 디바이스 오브젝트에 접근 할 수 있게 된다. 따라서 이후에는 I/O 매니저가 생성 또는 열기 작업을 마치기 위해 필요한 인스트럭션(Instruction)등을 수행 할 수 있다.

 

 

다음 칼럼에는

파일 시스템 컨셉과 코드에 대한 정의를 통해서 우리가 만들어야 하는 파일 시스템의 베니어 레이어의 역할, NT 커널 아래서의 파일 시스템의 위치, 레지스터 설정들을 이해 할 수 있었을 것이다. 이 아이디어들은 본격적으로 파일 시스템 구현에 들어감에 있어서 파일 시스템 드라이버가 해야 하는 특정 자료구조의 설정 및 플래그 변환 등의 논리적 순서를 이해하는데 큰 도움이 될 것이다.

 

다음 칼럼에서는 파일 시스템 드라이버가 반드시 책임져야 하는 자료구중 이번 칼럼에서 소개 되지 못한 것들을 소개하고 의사코드를 작성하는 시간을 가져 볼 것이다.

References

Rejeev Nagar, "Windows NT File System Internals": A Developer Guide, O'Reilly 1998

P. B. Kruchten."The 4+1 View Model of architecture."

David Garlan and Mary Shaw January 1994 "An Introduction to Software Architecture"

Kernel Source http://reactos-mirror.googlecode.com/svn

Kernel Source http://nuwen.net