본문 바로가기

Job Notes/File System

파일 시스템의 원리와 리눅스에서의 제어구조 1

파일 시스템의 원리와 리눅스에서의 제어구조

                                                                                           홍지만, 허선웅

(* 마이크로소프트웨어 2002년 11월호에 수록된 기사입니다.)

  파일 시스템이란 컴퓨터 운영체제(OS)에서 파일의 명칭 부여, 저장, 편성 등을 총괄적으로 유지 관리하는 구조 또는 체제로서 파일의 집합, 디렉토리, 위치 결정과 접근에 필요한 정보 등이 모두 포함된다. 즉, 디스크 장치를 관리하는 커널 기능이다(물론 파일 시스템도 구현에 따라서는 메모리를 관리하거나 태스크의 정보를 관리하도록 구현될 수도 있다. 예로 proc 파일 시스템을 들 수 있다).

디스크의 구조
  그렇다면 디스크란 무엇인가(너무 쉬운 질문인가)? 디스크란 우리가 흔히 볼 수 있는 플로피
디스크나 하드 디스크처럼 디지털 형식(2진 형식)으로 정보를 기록할 수 있는 기억매체다.

  하드 디스크는 원판(plotter)과 회전 축, 헤드(head)로 구성된다. 원판에는 원 모양의 트랙
(track)들이 존재하며 트랙은 다시 몇 개의 섹터(sector)로 구분된다. 그리고 모든 원판에서 같
은 위치에 있는 트랙의 집합을 실린더(cylinder)라고 한다. 섹터는 디스크에서 데이터를 읽거
나 기록할 때 기본 단위가 되며 일반적으로 섹터의 크기는 512바이트다. 한편 헤드는 각 원판
의 읽기(read)/기록(write)이 가능한 면마다 하나씩 존재한다. 따라서 헤드가 몇 개, 실린더 몇 개, 각 트랙마다 섹터가 몇 개 등이 결정되면 디스크의 전체 용량 등 해당 디스크의 물리적 특성을 결정할 수 있게 된다.

  여기서 자신의 하드 디스크를 간단하게 확인해 보자. 바이오스에서 자신의 하드 디스크의 실린더, 헤드, 섹터를 확인한 후 자신의 하드 디스크 용량과 맞는지 확인해 보자. 필자의 하드 디스크는 40GB로서 1만 9158개의 실린더와 16개의 헤드, 255개의 섹터로 구성되어 있다.

16(헤드 수)×19158(실린더 수)×255(섹터 수)×512바이트(섹터의 크기)=약 40GB

  한편, 디스크에 데이터를 읽거나 쓰기 위해서는 어떻게 해야 하는 것일까? 일단 해당되는 섹터로 접근한 후 데이터를 읽거나 써야 한다. 여기서 해당되는 섹터로 접근하는 시간을 접근 시간, 데이터를 읽거나 쓸 때 걸리는 시간을 데이터 전송 시간이라고 한다. 접근 시간은 탐색 시간(seek time), 회전 지연 시간(rotational latency)으로 구성된다. 탐색 시간이란 헤드를 요청한
데이터가 존재하는 트랙 위치까지 이동하는 데 걸리는 시간이고, 회전 시간은 요청한 섹터가 헤드 아래로 위치될 때까지 디스크 원판을 회전시키는 데 걸리는 시간이다.

디스크 블럭
  파일 시스템은 디스크를 물리적인 구조로 보지 않는다. 그 대신 파일 시스템은 디스크를 논리적인 디스크 블럭(disk block)의 집합으로 본다. 그렇다면 과연 어떻게 논리적인 블럭이 물리적인 블럭에 저장되는 것일까? 그 일은 파일 시스템이 관여하지 않고 장치 드라이버나 디스크 컨트롤러가 수행한다.

  디스크 블럭은 0, 1, 2 등의 논리적인 번호를 하나씩 갖는다. 그리고 디스크 블럭의 크기는 일반적으로 페이지 프레임의 크기와 같다. 인텔에서는 페이지 프레임의 크기가 4KB이며 따라서 디스크 블럭의 크기도 4KB가 된다. 최근에 개발된 파일 시스템은 디스크 블럭의 크기를 16KB, 64KB 등 더 크게 설정하는 경향이 있다. 만일 디스크 블럭의 크기가 4KB이고 섹터 크기가 512바이트라면, 결국 하나의 디스크 블럭에 여덟 개의 섹터가 대응하게 된다. 따라서 파일 시스템이 하나의 디스크 블럭을 읽어 달라고 요청하면 결국 여덟 개의 섹터 내용이 읽혀지게 된다.

블럭의 할당
  파일 시스템의 주요한 기능 중 하나는 파일에 데이터 저장 공간을 할당하고 회수하는 일이다. 예를 들어 디스크가 논리적으로 <그림 1>과 같이 생겼으며, 각 디스크 블럭의 크기가 4KB라고 가정하자. 그리고 <그림 1>에서 빗금으로 칠해진 디스크 블럭들은(블럭 번호 2, 3 등의 블럭들) 현재 사용 중(이미 존재하는 파일의 데이터를 담고 있음)이며, 빗금이 칠해지지 않은 블럭들(블럭 번호 1, 5, 6 등의 블럭들)은 현재 사용 중이 아닌 상태(free)라고 가정하자.

사용자 삽입 이미지

디스크의 논리적인 구조



  이와 같은 환경에서 파일 시스템에 7KB 크기의 새로운 파일을 생성해 보자. 파일 시스템은 디스크에서 free 상태의 디스크 블럭들을 할당받아 이 파일을 위한 공간으로 사용한다. 이번 예제에서는 파일 크기가 7KB이므로 두 개의 디스크 블럭이 필요하다(각 디스크 블럭의 크기가 4KB라고 가정했다). 현재 디스크에는 free 상태의 블럭이 1, 5, 6, 9, 10, 13, 14 등이 있다. 어떤 블럭들을 할당할 것인가?

  디스크 블럭을 할당하는 방법에는 크게 연속 할당과 불연속 할당, 두 가지 방법이 있다. 연속 할당이란 파일에 연속된 디스크 블럭을 할당하는 방법이다. 앞의 예에서 연속 할당 방법을 사용한다면 5와 6 디스크 블럭(또는 9와 10 디스크 블럭)을 할당할 수 있다. 반면에 불연속 할당 방법은 파일에 반드시 연속된 디스크 블럭을 할당할 필요는 없다. 앞의 예에서 불연속 할당 방법을 사용한다면 1과 5 디스크 블럭(또는 6과 9 디스크 블럭)을 할당할 수 있다.

  두 가지 방법 중에서 어떤 방법이 파일 시스템에 사용할 때 더 적당할까? 연속 할당은 불연속 할당에 비해 파일을 읽는 속도가 더 빠르다. 왜냐하면 디스크의 탐색 시간을 줄일 수 있기 때문이다. 하지만 연속 할당의 경우 파일 크기가 변하면 문제가 될 수 있다. 예를 들어 앞의 예에서 생성한 7KB의 파일에 사용자가 새로운 내용을 추가하여 그 크기가 9KB로 커졌다고 가정해 보자. 이제 이 파일은 세 개의 디스크 블럭을 필요로 하며, 따라서 파일 시스템은 이 파일에 새로운 디스크 블럭을 하나 더 할당해야 한다. 만일 5와 6 디스크 블럭을 처음 이 파일이 생성될 때 할당했다면 더 이상 연속적으로 할당할 수 없다. 결국 연속 할당을 위해서는 기존에 있던 디스크 블럭들을 더 넓은 곳(연속적으로 세 개 이상의 블럭이 free한)으로 복사한 후 새로운 내용을 추가해야 한다. 파일의 크기가 변하는 일은 매우 빈번하게 발생하는 일이다. 그리고 파일 크기가 커질 때 기존에 있던 블럭들을 다른 곳으로 복사하는 것은 성능상 매우 큰 문제가 된다. 따라서 파일 시스템을 설계할 때 연속 할당 방법을 사용하는 경우는 거의 없다.

  불연속 할당 방법은 같은 파일에 속한 디스크 블럭들을 연속적으로 저장하지 않는다. 따라서 7KB의 파일을 생성하는 예에서 1과 5 같은 디스크 블럭을 할당할 수 있다. 그리고 이 파일의 크기가 커져 새로운 디스크 블럭이 요구된다면 free한 6, 9, 10 등 어떤 블럭이라도 할당할 수 있다. 하지만 불연속 할당 방법에서는 파일에 속한 디스크 블럭들이 어디에 위치하고 있는지에 대한 정보를 기록해 두어야 한다. 이를 위한 방법으로는 블럭체인 기법, 인덱스 블럭 기법, FAT(File Allocation Table) 기법 등이 있다.

블럭체인 기법
  블럭체인 기법은 이미 사용 중인 블럭은 그대로 두고 나머지 사용되지 않은 블럭을 체인으로 만들어 파일을 저장하는 방식이다. 이러한 방식으로 블럭을 할당하면 순차적인 데이터 검색에는 많은시간이 소비될 수가 있으나 블럭의 효율적인 관리에는 매우 용이할 것이다.

사용자 삽입 이미지

블럭체인 기법


인덱스 블럭 기법
  인덱스 블럭 기법은 각각의 파일에 인덱스 블럭(index block)이라는 테이블을 만들어 그 파일에 속한 블럭을 인덱스 과정을 통해 접근하는 것이다. 이러한 방법으로 블럭을 할당한다면 그 파일에 속한 블럭을 상당히 빠른 속도로 찾을 수 있을 것이다. 그러나 만일 파일의 크기가 매우 커져서 지정된 인덱스 블럭 테이블(index block table)의 인덱스 개수를 넘어서는 블럭을 요구한다면 인덱스 블럭 테이블의 크기를 늘리는 등 상당히 복잡한 알고리즘을 요구할 것이다.

사용자 삽입 이미지

인덱스블럭 기법


FAT 기법
  FAT 기법은 보통 도스 파일 시스템에서 사용되는 방식이다. FAT이라는 테이블을 따로 만들어 블럭을 관리하는데, FAT 테이블의 내용을 직접 참조하면 어떤 블럭이 비어 있는지를 쉽게 알아
낼 수 있는 장점이 있다. 그러나 이 방식은 블럭 수만큼의 FAT 테이블 인덱스가 필요하기 때문에 메모리 활용도가 효율적이지 않을 수 있고 알고리즘이 복잡하다는 단점이 있다. <그림 4>는 FAT기법을 보여준다.

사용자 삽입 이미지

FAT 기법



  <그림 4>에서 보면 새 파일 이름이 FAT[1]의 내용을 가리킨다. FAT[1]은 5를 돌려주기 때문에 다음에는 FAT[5]로 포인터가 이동한다. 차례로 이동해 보면 FAT[6]→FAT[9]→NIL 순으로 포인터가 이동하게 된다. 즉, 이 파일은 블럭 5, 6, 9를 할당받게 됐다는 것을 알려주는 것이다. 이런 식으로 새로운 파일은 블럭을 할당받게 된다.
  지금까지 파일 시스템의 개념 및 일반적인 블럭 할당 기법을 살펴봤다. 지금부터는 좀더 구체적으로 들어가서 유닉스 파일 시스템에 대해 살펴보자.