NT 파일 시스템을 위한 자료구조 특성.
Characteristics of Data Structures for NT File System
NT 파일 시스템에서 파일 스트림을 관리하는 파일 컨트롤 블록(File Control Block, FCB)를 적절히 생성하고 이를 관리 하기 위해서는 파일의 닫기(Close)와 클린업(Cleanup)에 대한 차이를 이해하는 것이 필요하다. 이에 따라서 FCB를 메모리에서 해제 할 수 있기 때문에 FCB는 이에 대한 정보를 자신의 자료구조 필드(Field)에 저장해두는데 닫기와 클린업 동작자체가 매우 유사하기 때문에 이를 관리하는 오브젝트 매니저(Object Manager)와의 묵시적 통신 수단을 이해 해야 한다. 더불어 파일 시스템 구현에서 필요한 부가 기능과 그에 따른 자료구조의 특성들을 이해해보자.
정명수
|
필자는 지난 3년간 삼성전자에서 플래시 메모리와 관련된 연구와 임베디드 소프트웨어, 커널 드라이버 등을 개발 했었다. 현재는 조지아 공대(Georgia Institute of Technology) 컴퓨팅 칼리지에 재학 중이다. 글쓰기를 매우 좋아하며 학부시절에는 객체 지향 패러다임을 통하여 해석하는 프로그래밍 언어론에 관심이 있었으나 실무과정을 거치면서 컴퓨터 아키텍처로 관심사가 옮겨졌다. 최근에 관심 있는 분야는 운영체제, 파일시스템, 실시간 스케줄링 등이다.
저번 칼럼에서 우리는 이미 파일 컨트롤 블록을 정의하고 설계 하였다. 오늘 우리는 이 자료구조를 사용하면서 가장 많이 혼돈을 하게 되는 두 개의 카운터를 관리 하는 방법과 파일 컨트롤 블록의 할당 시점, 그리고 적절한 해제 시점이 언제인지를 구분하여 볼 것이다. 아울러 데이터 압축, 온-디스크(On-disk) 자료구조, 빠른 이름 찾기 캐시(Dynamic Name Lookup Cache)등의 기능을 정의하고 이에 대한 자료구조의 필요성을 조명해본다.
본 칼럼에서 간략하게 언급된 자료구조들은 이후 실제 구현에서 예를 보일 수 있도록 하겠다. 본 칼럼을 위해서 드라이버 개발에 익숙하지 않은 개발자라면 반드시 마이크로 소프트웨어 2월부터 언급 되었던 파일 시스템 구현의 기본 사항부터 다시 읽어보는 것이 바람직하다.
파일 컨트롤 블록(FCB) 구조체의 할당과 해제의 적절한 시점
디스크 비종속, 종속적 부분과 NT에 의해 요구되는 부분을 포함하여 바이트 스트림이 처음 열리고 해당 바이트 스트림에 대하여 시스템 메모리 상에 할당된 FCB가 존재 하지 않는 두 가지의 조건이 만족 될 때 파일 시스템 개발자는 FCB 구조체를 할당해야 한다. 예를 들어서 만약 imaso.log라는 파일이 시스템이 부팅 될 때 처음 참조 되었다면 해당 파일이 존재하는 논리적 볼륨을 관리하는 파일 시스템 드라이버는 IRP_MJ_CREATE에 의해서 호출자로부터 전달 되는 열기 요청을 받는데 이때 FCB 구조체를 할당할 수 있다.
만약 FCB를 생성 한 뒤에 다시 열기 요청을 받는다면 어떻게 처리 해야 할까? 해당 바이트 스트림에 대한 FCB 구조체가 시스템 메모리상에 존재 하는 한 그 뒤에 요청에는 FCB 구조체를 할당할 필요가 없다. (혼돈 하면 안 되는 것은 FCB가 전역에 하나만 존재하는 것이 아니라는 것이다. 말 그대로 각 바이트 스트림에 대하여 FCB가 하나씩 존재 한다고 보면 된다) FCB가 파일 시스템 드라이버에 의해서 시스템 메모리에 유지 되고 있는지 아닌지를 결정하는 것은 ReferenceCount필드이다. 이전 칼럼에서 언급 된 것처럼 FCB 구조체 설계는 파일 시스템 드라이버 개발자에 의해서 결정 되는 것이므로 실제 필드 이름은 각기 다를 수 있다. 우리 SFS예제에서는 _nRefCnt 가 ReferenceCount필드를 의미한다. 파일 시스템 드라이버가 FCB 구조체 생성을 취소하거나 실패 했을 경우, 이후에 발생하는 열기 요청에 대하여 처음 발생한 것이 아니더라도 다시 FCB를 생성 해주어야 한다.
파일 컨트롤 블록은 열려진 바이트 스트림의 정보를 관리하는 유일한 자료구조 이므로 이를 참조하는 NT 컴포넌트가 하나라도 있다면 파일 시스템 개발자는 FCB를 시스템 메모리상에 유지 해주어야 한다. ReferenceCount 필드가 하는 주요 역할은 FCB를 참조하는 수를 나타내는 것으로 ReferenceCount 필드가 0가 되면 파일 시스템 개발자는 FCB를 시스템 메모리상에서 해제 시켜도 된다. 다시 말해 해당 바이트 스트림을 참조하는 컴포넌트가 시스템에 없고 이를 사용하는 사용자가 존재 하지 않기 때문에 관련된 정보를 관하는 FCB를 없애도 된다는 것이다.
ReferenceCount 필드와 OpenHandleCount의 역할 차이.
이전 칼럼을 읽은 독자라면 이를 이해하고 있겠지만, 참조 수(RerferenceCount) 관리, 연 핸들 수(OpenHandleCount) 관리를 각각 맞고 있는 이 두 필드는 SFS예제에서 각각 _nRefCnt, _nOpenHdelCnt라는 이름으로 관리 되고 있다. 아무래도 이 둘간의 용도 차이가 헷갈릴 여지가 있는데, 이 둘간의 차이를 이해하는 것은 NT 커널 하에서 파일 시스템을 개발하는 데 매우 중요하다. 참조 수와 연 핸들 수에 대한 이 두 필드는 파일 시스템 드라이버 FCB 구조체를 시스템 메모리로부터 안전하게 해제하기 위하여 사용되며, 모두 파일 시스템 드라이버 내부의 FCB 구조체에 의해서 관리된다. 이 두 필드는 파일 시스템 개발자에 의해서 설계되고 사용 되기 때문에 다른 커널모드나 사용자 모드의 컴포넌트들에게는 보이지 않는다.
참조 수를 관리 하는 ReferenceCount(_RefCnt) 필드는 이 필드를 포함하고 있는 FCB 구조체 인스턴스를 외부에서 참조하고 있는 수를 나타낸다. 이 ReferenceCount가 0이 되지 않는 한 파일 시스템 드라이버 개발자는 FCB를 사용하고 있는 외부의 특정 모듈이 존재한다는 사실을 알 수 있으며 해당 FCB 구조체의 인스턴스를 시스템 메모리로부터 해제 할 수 없다는 것을 알 수 있다. 좀 더 구체적으로 ReferenceCount 필드가 참조 수를 유지 하는 방법을 알아 보자면 IRP_MJ_CREATE를 요청을 받을 때(다시 말해 파일 열기와 생성) 마다 파일 시스템 드라이버 개발자는 1을 증가 시킨다. 반대로 이 ReferenceCount 필드의 값을 감소 시키는 시점은 파일 시스템 드라이버가 IRP_MJ_CLOSE를 받았을 때 이다.
파일 시스템이 ReferenceCount에 따라 이를 꼭 관리해주여야 하는 이유는 무엇일까? 이는 NT I/O 매니저 또한 파일 시스템 드라이버가 참조 수를 관리하고 생성 시에는 이를 증가, 닫을 시에는 이를 감소 할 것이라는 것을 가정하여 동작 한다는 것이다. 이러한 파일 시스템 드라이버의 참조 수 자체를 NT I/O 매니저가 고려할 뿐만 아니라 이 참조 수를 관리 하는 필드가 0가 되면 파일 시스템이 메모리를 해제 할 것이라는 것도 고려 하고 있다. 이러한 파일 시스템의 동작 방식은 I/O 매니저와 다른 윈도우 NT 컴포넌트들에게도 반영 되어 있다. 앞서 언급 한 것처럼 FCB 구조체와 그 인스턴스는 파일 시스템에서 다른 커널 컴포넌트로 외부공개 되어 있지 않지만, I/O 매니저와 다른 윈도우 컴포넌트들은 파일을 열고 닫을 때 사용되는 IRP 요청을 통해서 파일 시스템의 참조 수 관리 필드를 간접적으로 조절 할 수 있다. 따라서 NT 시스템 컴포넌트들은 파일 시스템 드라이버의 적절한 행동을 예상할 수 있고, 또한 파일 시스템 드라이버에 의해서 FCB가 시스템 메모리에 유지되는 시간을 조절 할 수 있다. 다시 말하면 파일 시스템 개발자는 이렇게 암묵적으로 이루어지는 메커니즘을 따라 참조 수를 관리하고 FCB를 유지 해주어야 한다.
<그림 1, CLEANUP과 CLOSE 입출력 요청 패킷(IRP)의 사용 예>
열려진 핸들 수(OpenHandleCount)는 FCB를 위해 외부의 사용자 핸들의 수를 관리 한다. 이 필드 또한 IRP_MJ_CREATE에 의해 증가된다. 그림 1에서 알 수 있듯이, OpenHandleCount(_OpenHdleCnt)는 참조 수를 관리하는 ReferenceCount와 달리 IRP_MJ_CLOSE에 따라 감소 하는 것이 아니라 IRP_MJ_CLEANUP에 의해서 감소 된다. IRP_MJ_CLEANUP 요청은 NT I/O 매니저에 의해서 관리 되는데 사용자 프로세스가 마지막 시점에 파일 핸들을 닫으면 이를 파일 시스템 드라이버에게 전달한다. 마지막 시점이라는 것은 모든 해당 파일을 열고 있던 사용자의 인스턴스들이 없어서 그 카운트가 0이 될 때를 의미한다.
파일 오브젝트 타입의 구조체는 윈도우 실행부(Executive)에서 정의된 자료구조로서 NT 오브젝트 매니저(Object Manger)에 의해서 관리 된다. 파일 오브젝트(File Object) 구조체에 대하여 오브젝트 매니저는 두 개의 카운터(Counter)를 유지한다. 이 카운터와 관련된 필드들의 이름들은 정확히 외부에 들어 나있지 않기 때문에 본 칼럼에서는 파일 시스템 개발의 대가인 Rajeev Nagar가 정의하는 가상의 이름으로 이를 대신 하겠다. 하나는 파일 오브젝트와 관련이 있는 하나 이상의 한들을 소유하고 있는 프로세스마다 이를 관리하는 프로세스 핸들 카운트(ProcessHandleCount)이고 또 다른 하나는 프로세스 핸들 카운트의 총 수를 더해 놓은 것과 같은 시스템 핸들 카운트(SystemHandleCount)라는 것이다. 이 두 가지 핸들 카운터 이외에 추가적으로 NT 오브젝트 매니저는 모든 오브젝트에 대하여 오브젝트 참조 카운트(ObjectReferenceCount)라는 것을 가지고 있다. 이 참조 카운트는 프로세스 핸들 카운트나 시스템 핸들 카운트 중 하나가 증가하면 같이 1씩 증가한다. 여기서 주의 해야 할 점은 프로세스 핸들 카운트나 시스템 핸들 카운트가 증가하지 않아도 간혹 오브젝트 참조 카운트가 증가하는 경우가 있다는 것이다. 예를 들면 커널 모드의 컴포넌트들의 오브젝트를 참조 할 때는 해당 오브젝트의 핸들을 요구하지 않고 직접 관여하기 때문에 이때는 시스템 핸들이나 프로세스핸들 카운트가 증가 하지 않는다.
특정 프로세스가 소유한 핸들을 ZwClose()함수나 NtClose()함수를 통해서 닫을 때에는 NT 오브젝트 매니저가 해당 오브젝트에 대한 파일 프로세스 핸들 카운트, 시스템 핸들 카운트 모두를 하나씩 감소 시킨다. 파일 오브젝트 경우에 닫는 절차 시에는 I/O 매니저에 의해서 제공되는 IopCloseFile()함수를 호출 하는데 오브젝트 매니저는 프로세스 핸들 카운트와 시스템 핸들 카운트 필드를 IopCloseFile()함수에게 전달하여 제공한다. IopCloseFile()함수는 외부에 존재하는 모든 사용자 핸들이 닫히는 경우 내부적으로 IRP_MJ_CLEANUP을 파일 시스템에게 요청하고 만약 시스템 핸들 카운트가 1이라면 외부 사용자 핸들에 의한 참조가 단 하나만 존재 하는 것이므로 NtClose()함수를 호출 한다.
IopCloseFile()함수가 한번 호출 되어 처리되고 나면 파일 시스템 드라이버 쪽에서도 해당 IRP_MJ_CLEANUP처리를 끝냈을 것이므로 오브젝트 매니저는 오브젝트 참조 카운트를 감소 시킨다. 만약 이 참조 카운트가 0이라면 오브젝트 매니저는 해당 오브젝트를 IoDeleteFile()함수를 사용하여 삭제한다. 비록 파일 시스템 드라이버가 사용자 핸들이 닫힐 때 마다 클린업(Cleanup)과 관련된 IRP를 받더라도 해당 파일 시스템 드라이버는 IRP_MJ_CLOSE를 받기 전에는 해당 FCB를 해제 할 수 없다는 것을 명심해야 한다.
UNIX 계열의 경우도 IRP_MJ_CLEANUP과 IRP_MJ_CLOSE와 같은 형태의 요청으로 파일에 관련된 참조들을 관리하는데 IRP_MJ_CLEANUP과 같은 것이 vnode close이며 마지막에 전달되는 IRP_MJ_CLOSE는 비 활동 요청(Inactivate) 동작으로 vnode 구조를 관리한다. 그렇다면 파일 시스템 드라이버가 관리하는 FCB 구조체의 두 필드, 참조 수와 열려진 핸들 수를 가지고 어떤 것들을 더 유추해 낼 수 있을까?
우선 얼마나 많은 사용자 핸들들이 FCB를 참조 하고 있을 지 생각 해보자. 다른 말로 하면 파일 시스템 드라이버 개발자는 IRP_MJ_CLEANUP를 아직 받지 않은 IRP_MJ_CRATE 요청의 총 수에 대한 아이디어를 가지고 있어야 이를 처리 할 수 있다. 이 숫자가 0이 아닌 이상 파일 시스템 드라이버는 적어도 하나의 스레드가 FCB에 의해서 지시된 파일 스트림의 유효한 핸들을 가지고 있다는 것을 알 수 있고 이를 위해서 FCB를 그때까지는 시스템 메모리상에 상주 시켜두어야 하는 것을 유추 할 수 있다. 이 사실은 ReferenceCount가 0이 아니면 삭제를 할 수 없는 요구사항과는 또 다른 것이다.
새로운 파일 오브젝트가 I/O 매니저에 의해 생성되는 동작에 따라 파일 시스템 드라이버의 FCB의 오브젝트 핸들 카운트가 증가한다고 하더라도 해당 파일 오브젝트에 대하여 시스템 전체의 핸들 수를 관리 할 필요는 없다. 사용자 파일 핸들이 하나의 프로세스 내에 여러 스레드상에 복사 되거나 파일 핸들이 부모 프로세스로부터 상속 될 때 마다, 그리고 특정 프로세스가 파일 오브젝트 포인터에 생성을 요청할 때마다 NT 오브젝트 매니저는 시스템 핸들 카운트를 증가 시킨다. 파일 시스템 드라이버는 이러한 파일 핸들에 대한 복사나 상속에 대하여 어떠한 정보도 받지 않기 때문에 열려진 핸들 수(OpenHandleCount)는 이러한 상속, 복사 등의 작업에 의해 증가 되지 않는다. 자신도 모르는 사이에 파일 오브젝트에 접근한 핸들들이 증가하는데 파일 시스템 드라이버 입장에서 그럼 열려진 핸들 수를 관리하는 것에 의미가 있는 것인가? 파일 시스템 드라이버는 이러한 상속, 복사 등의 작업에 의해서 통지 받는 정보가 전혀 없더라도 문제가 없다. 왜냐면 그림 2에서처럼 이렇게 파일 오브젝트 참조를 획득한 것들은 I/O 매니저가 해당 사용자 핸들들이 모두 닫히기 전에는 IRP_MJ_CLEANUP 요청을 보내지 않기 때문이다. 그래서 파일 시스템 드라이버는 항상 자신에게 생성, 또는 열기 요청을 받은 핸들에 대해서만 클린업 요청을 받으며 해당 클린업 요청에 대하여 열려진 핸들 카운트를 감소 시키기만 하면 된다.
<그림 2, 열려진 핸들 수 관리와 IRP_MJ_CLEANUP이 이슈(Issue)되는 시점>
FCB 구조체를 위한 외부 참조가 얼마나 있는지 확인하려면 어떻게 해야 할까? 참조 수를 관리하는 ReferenceCount는 FCB 인스턴스의 외부 참조의 전체 수를 파악하는데 도움을 준다. 재미있는 점은 열려진 핸들 수(OpenHandleCount)가 0이 된지 한참이 지나서 ReferenceCount가 0이 될 수도 있다는 것이다. 이 것은 모든 FCB에 대한 모든 사용자 핸들을 닫더라도 특정 커널 모드 컴포넌트들은 FCB를 시스템 메모리상에 상주시키길 원한다는 것으로 해석 할 수 있다. 일반적으로 이런 상황은 그림 3에서처럼 NT 캐시 매니저와 NT 가상 메모리 매니저가 함께 특정 파일데이터를 사용자가 닫았다고 하더라도 이를 메모리 안에 캐싱 시켜 놓고자 할 때 발생한다.
NT 가상 메모리 매니저와 캐시 매니저가 파일 데이터를 메모리에 상주 시켜 놓으려고 하는 것은 사용자가 이 파일에 대해서 다시 접근할 때 가능한 빨리 응답하도록 기회를 주어 성능을 향상하려는 것이다. 가상 메모리 매니저와 캐시 매니저는 FCB 인스턴스가 메모리상에 없으면 해당 파일을 접근 할 수 있는 방법이 없으므로 이를 고려하여 동작하는 것이다. 이러한 캐싱기법은 어떻게 보면 단순히 소모적인 행동으로 보여 질 수 있으나 실제 많은 사용자들이 한번 접근한 파일 스트림에 대해서 다시 접근하는 경향이 있으므로 (지역성, Locality of cache) 해당 FCB를 당분간 유지하고 이를 메모리에 두어 디스크로의 접근을 막고 성능을 향상시키는 기법은 실제로 꽤 효과가 있는 것으로 알려져 있다. 그렇다면 어떻게 가상 메모리 매니저나 다른 NT 커널의 컴포넌트들이 FCB를 메모리에 그대로 유지 시키게 할 수 있을 것인가? 이러한 암묵적 커뮤니케이션 기법들은 파일 시스템 개발자가 반드시 알아야 할 것들이다. 왜냐면 NT 커널 컴포넌트들이 이러한 방법으로 성능 향상을 꾀하고 있는데 파일 시스템 개발자가 자신의 파일 시스템 구조 자체에만 신경을 쓴다면 전체 시스템의 성능이 떨어질 수 밖에 없다. NT 커널의 컴포넌트들은 FCB를 메모리에 유지 시켜놓기 위해서 파일 시스템이 ReferenceCount와 OpenHandleCounte를 참조 하는 행동을 고려하여 파일 오브젝트를 강제적으로 참조한다. 이러한 작업을 통하여 NT 커널의 컴포넌트들은
사용자들이 해당 파일 오브젝트를 닫았다고 하더라도 오브젝트 매니저가 해당 오브젝트를 닫는 요청을 통지하지 못하도록 막는다. 그 결과, 파일 시스템 드라이버는 FCB의 참조 수를 감소 시키지 못하고 0이 되지 않아서 FCB 인스턴스를 메모리상에서 해제 하지 않게 된다.
<그림 3, 참조된 수와 열려진 핸들 수가 차이가 생기게 되는 경우>
다른 기타 주요 구조체
이제까지 언급된 파일 시스템의 자료 구조 이외에도 파일 시스템 개발자가 자신의 파일 시스템의 기능을 구현하기 위해서 보조, 또는 관리 해야 하는 몇몇 중요한 자료구조들이 존재한다. 파일 시스템 개발자가 부가적으로 관리 해야 하는 기능과 자료구조는 아래와 같이 크게 가지로 나뉘어 진다.
- 바이트 단위의 락 메커니즘(Byte-range Locks)
- 동적 이름 찾기 캐시(Dynamic Name Lookup Cache)
- 파일 스트림과 디렉터리 할당량 관리(File Stream and Directory Quotas)
- 기회적 락 메커니즘(Opportunistic Locks)
- 디렉터리 변경 통지 기능(Directory Change Notification)
- 데이터 압축과 암호화 기능(Data Compression, Encryption and Decryption)
- 빠른 리커버리 로깅 지원(Recovery Logging)
- 온-디스크 자료구조 지원(On-disk Data Sturucture)
이 단락부터는 그러한 자료구조들과 특성을 살펴 보기로 하자.
바이트 범위의 락 메커니즘
대부분의 파일 시스템들이 바이트 범위의 락 메커니즘을 제공하는데 이러한 락들은 필수적인 기능과 그렇지 않은 경우 둘 모두에 대해서 구현 될 수 있다. 필수적인 기능은 NT 파일 시스템 드라이버를 위한 기능 명세에 포함 되는 것으로 반드시 제공되어야 한다. 다시 말해서, 만약 한 스레드가 특정 바이트 범위의 락을 요구한다면 윈도우 NT 계열아래의 파일 시스템 드라이버 개발자는 같은 파일 내에 같은 바이트 범위에 대해서 사용을 요구하는 모든 스레드를 락킹할 수 있어야 한다. 필수적인 락 메커니즘과 다르게 보조의 역할을 하는 락 메커니즘도 제공할 수 있다. 이 보조의 바이트 범위 락 메커니즘은 두 개의 다른 프로세스가 특정 파일 스트림의 바이트 범위 내에서 동시에 접근하며 서로 협력해야 하는 작업이 있을 때 동기화 기능을 제공한다. 주의해야 할 것은 설사 파일 시스템 개발자가 필수적 락 메커니즘 대신에 보조적 바이트 범위의 락 메커니즘을 제공하더라도 실제 윈도우 NT 플랫폼은 보조적 바이트 범위의 락 메커니즘을 효율적으로 제공하지 못한 다는 것이다. 따라서 만약 이러한 형태로 락 메커니즘을 제공하려고 하는 파일 시스템 개발자가 있다면 구현에 있어서 최대한 주의를 기울여야 한다.
우리가 이러한 바이트 범위의 락 메커니즘을 지원하는 파일 시스템을 구현 하기 위해서는 반드시 이 기능을 지원하기 위한 파일 컨트롤 블록과 컨텍스트 컨트롤 블록(FCB/CCB)에 관련된 자료구조들을 메모리상에 유지 해야 한다는 것이다. 실제 락 메커니즘에 대해서는 이후 SFS 파일 시스템 구현 예제에서 한번 더 언급하기로 하자.
동적 이름 찾기 캐시 구현
만약 UNIX 파일 시스템을 공부한적이 있는 독자라면 동적 이름 찾기 캐시(Dynamic Name Lookup Cache, DNLC)의 아이디어에 익숙할 것이다. DNLC는 디렉터리 내에서 최근에 액세스된 파일들의 캐시이다. 최근에 접근하여 온-디스크 메타 정보를 읽어온 파일 이름들은 특정 디렉터리 내에 특정 파일 이름을 빨리 찾기 위해 대부분의 경우 DNLC는 해시 리스트 자료구조로 구성한다. 대부분의 파일 시스템 구현에서 디렉터리 내에 특정 파일을 찾기 위해서 선형 검색 알고리즘을 사용하는데 DNLC는 최근에 접근한 파일 이름을 찾기 위해서 이러한 선형 검색을 피하고 속도를 증가 시키는 역할을 한다. (캐시라는 이름도 이러한 이유로 유추 할 수 있다)
정확히 이야기하면 DNLC를 구현하는 것은 필수사항은 아니다. NT 커널을 포함 대부분의 운영체제는 파일 시스템 드라이버 개발자가 DNLC를 지원하는 것에 대하여 특별히 신경을 쓰지 않는다. 하지만 성능상의 가급적 DNLC의 구현을 고려 해주는 것이 좋다. 리눅스 커널에 익숙한 사람이라면 한번 생각 해보라. 가장 많이 콘솔에 치는 명령어가 무엇인가? 디렉터리 내에 파일을 찾아내는 "ls" 이다. 어떤 경우는 해야 할 일이 없을 때 아무 없이 "ls"를 콘솔 창에 치기도 하니까 말이다.
파일 스트림과 디렉터리 할당량 관리
NT 커널이 과거 파일 스트림과 디렉터리, 논리적 볼륨에 대하여 할당량을 관리 하지 않았지만, NT 버전 5.0부터 이를 지원한다. UNIX 계열의 운영체제의 디스크 할당량 관리처럼 파일 스트림, 디렉터리에 대한 할당량 관리를 지원하려면 파일 시스템 개발자는 반드시 이에 관련된 적절한 자료 구조를 인-메모리, 온-디스크 모두에 유지 해야 한다. 물론 이러한 기능이 제대로 지원을 적절히 구현하지 못하였다면 이러한 기능을 하는 필터 드라이버를 제작하여 기존 파일 시스템 드라이버를 지원 할 수 있다.
기회 락킹 메커니즘.
기회 락(Opportunistic Lock, oplock)은 윈도우 기반의 플랫폼에서 네트워크를 관리하는 LAN 매니저의 기능 중 하나이다. 기본적으로 기회 락은 서버가 자신의 클라이언트들에게 자신의 지역 디스크를 공유할 수 있게 해준다. 이를 통하여 서버는 클라이언트에게 특정 파일 스트림이 서버에 의해서 변경 되지 않는다는 것에 대하여 정보를 전달 할 수 있고, 클라이언트는 서버에 의해 파일의 변경이 필요 한 경우 변경이 일어나기 전에 이에 대한 사실을 확인 할 수 있다.
oplock으로 불리는 기회 락의 이러한 기능을 이용하여 클라이언트는 파일 스트림의 캐싱을 안전하게 할 수 있기 때문에 원격 파일 스트림 접근에 대한 응답시간을 최대한 줄일 수 있다. 만약 이러한 기능이 제공되지 않는다면 클라이언트가 캐시를 하는 동안에 서버가 이러한 데이터를 변경하게 되어 비 일관성 문제를 야기 할 수 있기 때문에 클라이언트는 매번 동기화 과정을 거쳐야 하는 오버헤드를 감수 해야 한다. oplock은 파일 시스템 구현에 필수적인 항목은 아니지만, 만약
네트워크 리렉터리를 개발하고 있는 것이 아니고 자신의 파일 시스템이 앞서 언급된 LAN 매니저와 논리적 볼륨을 공유 해야 한다면 oplock 구현을 반드시 고려해야 한다. oplock은 DNLC처럼 필수 요구사항은 아니지만 구현되지 않았을 경우 공유된 논리 볼륨의 접근에 대해여 심각한 성능저하를 가져 올 수 있다.
디렉터리 변경 통지 기능
디렉터리 변경 통지 기능은 윈도우 운영체제와 I/O 매니저가 필요로 하는 또 다른 종류의 기능이다. 대체적으로 디렉터리 변경 통지는 유저 모드나 커널모드의 컴포넌트들이 특정 디렉터리나 디렉터리 트리들의 변경을 모니터링 하는데 유용하게 쓰인다. 이러한 컴포넌트들은 자신이 원하는 변경이 어떤 것인지 (예를 들면 파일의 생성에 대한 모니터만을 원한다던가, 수정 작업에 대해서만 모니터를 원한다던가 하는) 정확히 기술 할 수 있고 이를 이용해서 I/O 매니저에게 이를 요구할 수 있다. 요청을 받은 I/O 매니저는 기술된 변경 사항이 감지되면 비동기적으로 이에 대해 알아 낼 수 있도록 파일 시스템을 질의한다. 이러한 디렉터리 변경 통지 기능은 많은 어플리케이션으로 하여금 특정 디렉터리의 변경을 감지 하기 위해 단순히 계속 정보를 질의하며 시간을 보내는 비효율적인 방법을 피하게 해준다. 물론 이러한 디렉터리 변경 통지도 파일 시스템에게 필수적으로 구현되어야 하는 요구사항은 아니지만 모든 NT계열의 파일 시스템들은 이를 지원하고 있기 때문에 파일 시스템 개발자는 디렉터리 변경 통지 기능의 구현을 고려해주어야 한다.
데이터 압축
NTFS는 데이터 압축을 기본 기능으로 제공하고 있다. 따라서 파일 시스템 개발자는 데이터 압축 기능을 구현 해야 할 필요가 있으며 필요에 따라 온라인(Online) 데이터 압축 메커니즘을 제공해야 할 수도 있다. 데이터 압축 기능을 제공하기 위해서 파일 시스템 드라이버 개발자는 온-디스크 와 인-메모리 자료구조를 이용하여 특정 파일 스트림이 압축 되었는지를 기술 할 수 있어야 하며 만역 압축 되었다면 원래 파일 스트림의 사이즈는 얼마였는지 또 이를 관리하기 위한 정보들을 다룰 수 있어야 한다.
NT I/O 매니저는 파일 시스템 개발자에게 데이터 압축을 지원하기 위해 사용자 프로세스가 압축된 형태의 파일 스트림을 요구할 수 있도록 하는 시스템 콜 인터페이스(System Call Interface)를 제공한다. I/O 매니저가 사용자 프로세스로 하여금 압축 제어에 관련된 정보와 실제 파일 스트림의 사이즈, 압축된 파일 스트림의 사이즈를 질의 할 수 있게 해 놓았기 때문에 파일 시스템 개발자는 이에 대한 정보를 반드시 유지 해주어야 한다.
암호화 기능
개발해야 하는 파일 시스템의 특성에 따라 저장된 데이터에 대하여 동적으로 암호화와 이에 대한 해독을 해야 할 수 도 있다. 당연히 이러한 요구사항을 만족 시키기 위해 이를 구현하는 파일 시스템 개발자는 반드시 암호화, 해독에 관련된 정보를 관리하는 자료구조를 설계, 유지 해주어야 한다. 만약 파일 시스템 개발 당시에 암호화 기능을 구현하지 않았는데 추후에 이를 구현 해주어야 한다면 해당 파일 시스템 자체를 변경 시키지 않고 필터 드라이버를 파일 시스템 위에 상주시켜 이 기능을 대신 수행하게 할 수 도 있다.
빠른 리커버리(Recovery)를 위한 로깅(Logging) 작업
잘 알려진 바와 같이 NTFS는 예상치 못했던 시스템 실패에 대해서 빠른 리커버리 로깅(Recovery Logging)을 위한 인-메모리와 온-디스크 자료구조를 사용한다. 만약 독자가 로그 기반의 파일 시스템을 개발해야 한다면 로그 파일 스트림과 로깅을 위한 적절한 인-메모리, 온-디스크 자료구조들을 설계 하여 관리 해야 한다. 로그 기반의 파일 시스템과 저널링 기법들에 대한 연구는 이제까지 활발하게 이루어져 왔다. 이 중에서 가장 근간이 되는 것은 버클리 대학의 Mendel Rosenblum 와 John K. Ousterhout이 작성한 "The Design and Implementation of a Log-Structured File System"으로 로깅 작업을 지원해야 한다면 이 논문을 읽어보는 것이 가장 바람직하다.
온-디스크 자료구조 지원
일반적으로 네트워크 네트워크 리디렉터, 데이터 베이스 파일 시스템들은 온-디스크 파일 스트림을 관리 하기 위하여 온-디스크 자료구조들을 사용해야 한다. 여기는 디렉토리의 컨텐츠(Contents)들을 담고 있는 디렉토리 엔트리 자료구조, 온-디스크 FCB, inode, vnode니 비트맵(Bitmap), 볼륨 정보와 같은 것들이 필요 할 수 있다. 이러한 온-디스크 자료구조 지원은 비록 네트워크 리디렉터나 데이터 베이스 이외에도 플래시를 사용하는 파일 시스템, 새로운 포맷 형태의 파일 시스템을 개발하는 개발자라면 온-디스크 자료구조를 설계 관리 해야 한다. 또한 이러한 형태의 파일 시스템 개발자는 온-디스크 자료구조를 저장하고 읽어오는 시점을 잘 결정해주어야 하며 이를 인-메모리 자료구조로 변경 시켜 메모리상에서 관리 할 수 있도록 하는 방법을 제공해야 한다.
다음 칼럼에는
자료구조는 명시적으로 그것에 관여하는 컴포넌트들과 알고리즘, 일정한 형태의 루틴뿐만 아니라 암묵적으로 관여되는 커뮤니케이션 방법을 모두 담고 있다. 특히나 세부적인 요구사항이 다양하고 복잡하게 얽힌 운영체제를 대부분이 레이어드(Layered) 아키텍처를 사용하여 설계, 유지 보수를 하고 있다. 이러한 레이어드 아키텍처에서는 직,간접적으로 각 컴포넌트들간에 자료구조 사용에 간섭이 존재 한다. 간혹 자료구조와 암묵적 통신 방법을 이해하지 못하는 경우 필요한 코드를 써 내려가면서 실제로 왜 이러한 코드가 자신의 드라이버 안에 있는지 모르는 경우가 종종 있다.
우리는 우선적으로 눈에 보이는 자료구조들을 정의하고 이에 필요한 통신 지식들을 습득 함으로써 실제 구현에 들어갔을 때 전반적으로 이를 이해 할 수 있도록 이에 대한 내용을 칼럼상 선 배치 하였다. 다음 칼럼부터는 디스패치 루틴 중 드라이버의 진입점을 위한 코드 설정, 열기, 읽기 둥의 기본 동작들을 정의하고 이러한 동작들에 대한 간략한 설명과 함께 실제 구현 예를 살펴 볼 것이다.
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
'Drafts > Kernel mode' 카테고리의 다른 글
| Characteristics of Data Structures for NT File System (0) | 2010/05/06 |
|---|---|
| Major Control Blocks and Design for NT File System Implementation (0) | 2010/04/20 |
| Basic Functionalities and Concepts for File System Implementation (0) | 2010/03/17 |
| Communication between Virtual Memory Manager and File System (0) | 2010/02/17 |
| The Virtual Address Translation with Considering MMU and TLB (0) | 2010/01/05 |
| NT Virtual Memory Manager Overview (0) | 2009/12/13 |