파일 시스템은 데이터와 프로그램을 포함하여 파일 내용의 온라인 접근과 온라인 저장을 위한 기법을 제공하며, 보조저장장치에 영구적으로 상주한다. (13장 참고)
이번 14장에서는 하드 디스크 드라이브와 비휘발성 메모리 장치상의 파일 저장과 접근을 둘러싸고 있는 이슈들을 주로 다룬다.
파일 시스템을 유지하기 위한 보조저장장치로 디스크가 대부분 사용된다. 아래 2가지 특성 때문이다.
1. 디스크는 추가 장소를 사용하지 않고 재기록이 가능하다.
2. 디스크에 있는 임의의 블록의 정보를 직접 접근할 수 있다. 따라서 임의의 파일을 순차적 또는 무작위 방법으로 쉽게 접근할 수 있다.
비휘발성 메모리(NVM) 장치는 파일 저장 및 파일 시스템의 장소로 점점 더 많이 사용되고 있다. 하드디스크와는 달리 재기록이 불가능하며 성능 특성이 다르기 때문이다.
✅ 파일시스템
📌 I/O control (입출력 제어 층)
- 장치 드라이버 루틴들과 인터럽트 핸들러로 이루어져 있어서 메모리와 디스크 시스템 간의 정보 전송을 담당한다.
(장치 드라이버는 번역기라고 생각할 수 있다. 입출력 제어기 메모리의 특별한 위치에 특정 비트를 설정하여 제어기에 어느 장치에 어떤 일을 수행할 지를 알린다.)
📌 basic file system (기본 파일 시스템)
- 적절한 장치 드라이버에게 저장장치상의 블록을 읽고 쓰도록 일반적인 명령을 내린다.
- I/O 요청 스케줄링을 고려한다.
- 다양한 파일 시스템, 디렉터리 및 데이터 블록을 저장하는 메모리 버퍼와 캐시를 관리한다.
📌 file-organization module (파일-구성 모듈)
- 파일 구성 모듈은 파일과 상응하는 논리 블록을 알고 있고, 어느 디스크 공간이 비어 있는지를 파악하는 가용 공간 관리자도 포함하고 있어서 이 모듈이 요구할 때 이들 블록을 제공한다.
📌 logical file system (논리 파일 시스템)
- 메타데이터 정보를 관리한다.
(메타 데어터는 파일의 내용 자체인 데이터를 제외한 모든 파일 시스템 구조를 말한다.)
📌 file control block, FCB (파일 제어 블록)
- 소유, 허가 그리고 파일 내용의 위치를 포함하여 파일에 관한 정보를 가지고 있다.
파일 시스템 연산을 구현하는데 사용되는 구조와 연산에 대해 깊이 살펴보자.
파일 시스템은 저장장치에 저장된 운영체제를 어떻게 부트시키는지, 그리고 블록의 총수, 가용 블록의 수와 위치, 디렉터리 구조 그리고 개별 파일에 대한 정보를 디스크 상에 가지고 있다.
✅ 디스크 상의 구조
메모리 내의 정보는 파일 시스템 관리와 캐싱을 통한 성능 향상을 위해 사용된다. 이 정보들은 마운트 시점에 적재되고, 파일 시스템 동작 중에 갱신되며, 마운트 해제 시에 제거된다.
✅ 메모리 내의 정보
정리하자면
➡ 새로운 파일을 생성하기 위해,,
1. 프로세스는 논리 파일 시스템을 호출한다.
2. 파일 시스템은 새로운 FCB를 할당하고, 해당 디렉토리를 메모리로 읽어, 새로운 파일 이름과 FCB로 디렉토리를 갱신하여, 파일 시스템에 다시 쓴다.
3. 그리고 파일 시스템은 디렉터리 입출력을 저장장치 블록 위치로 매핑하기 위해 파일 구성 모듈을 호출하고
4. 이것은 기본적인 파일 시스템과 입출력 제어 시스템에서 처리된다.
✅ open() system call & read() system call
디렉토리 공간을 어떻게 할당하고 관리하는가는 파일 시스템의 효율, 성능과 신뢰성에 큰 영향을 미친다.
디렉터리를 구현하는 가장 간단한 방법.
파일 이름과 데이터 블록에 대한 포인터들의 선형 리스트를 디렉터리에 사용한다.
이 방법은 프로그램이 쉽지만 실행 시간이 길다. (파일을 찾기 위해 선형 탐색을 해야 하므로)
✔ 파일 생성
: 먼저 디렉터리를 탐색하여 같은 이름을 가진 파일이 존재하지 않는다는 것을 확인한 후, 디렉터리의 끝 부분에 새로운 항목을 첨가한다.
✔ 파일 삭제
: 디렉터리에서 이름을 찾아 그 파일에 할당된 공간을 방출한다.
✔ 파일 재사용
: 항목을 미사용으로 표시하거나 가용 디렉터리 항목 리스트에 추가한다.
파일 이름을 제시하면 해시로부터 값을 얻어서 그것을 포인터로 활용하여 이 리스트를 직접 접근할 수 있다. ➡ 디렉터리 탐색 시간을 상당히 개선할 수 있다.
✔ 해시 테이블의 심각한 문제점
해시 테이블이 고정된 크기를 갖는다는 점
해시 테이블의 크기에 따라 해시 기능도 제한을 받는다는 점
(예) 64개의 항목을 갖는 선형 탐사 해시 테이블을 만든다고 가정했을 때, 해시 함수는 64로 나눈 나머지 값을 이용하여 파일이름을 0부터 63까지의 정수로 변환한다. 나중에 65번째 파일을 생성하려면 디렉터리 해시 테이블을 (이를테면 128 항목이 들어가도록) 반드시 키워야 하고, 기존 디렉터리를 새로운 해시 값에 맞게 새로 조직해야 한다.
✔ 대안으로, 체인 오버플로우 해시 테이블을 사용할 수 있다.
: 각 해시 항목은 하나의 값이 아니라 연결 리스트가 되고, 새로운 항목을 연결 리스트에 추가함으로써 충돌을 해결한다.
파일을 어떻게 저장장치 공간에 배치해야 디스크 공간을 효율적으로 사용할 수 있고, 파일들을 빨리 접근할 수 있을까?
Contiguous Allocation (연속할당)
- 각 파일이 저장장치 내에서 연속적인 공간을 차지하도록 요구한다.
- 한 파일의 연속 할당은 첫 번째 블록 주소와 (블록 단위의) 길이로 정의된다.
(파일 길이가 n 블록이고 블록 b에서 시작한다면, 이 파일은 블록 b, b+1, b+2, ,,, , b+n-1을 차지한다)
✔ 장점
✔ 단점
파일의 가용 공간을 찾기가 어렵다.
(조금 있다가 설명할 가용 공간 관리 시스템의 구현이 이 문제를 해결)
파일을 위해서 얼마나 많은 공간을 주어야 할지 결정하는 것이 어렵다.
➡ 너무 작은 공간을 예약했을 경우
운영체제는 이러한 단점을 최소화하기 위해 어느 정도의 연속된 공간만 초기에 할당하고 그 양이 충분히 크지 않을 때는 추후 n개의 연속된 공간을 단위로 할당한다.
Linked Allocation (연결할당)
- 파일은 저장장치 블록의 연결 리스트 형태로 저장되고, 이 블록들은 장치 내에 흩어져 저장될 수 있다.
- 디렉터리는 파일의 첫번째와 마지막 블록에 대한 포인터를 가지고 있다.
- 각 블록은 다음 블록을 가리키는 포인터를 포함한다.
(포인터가 차지하는 영역은 사용자가 사용할 수 없다. 따라서 블록 크기에서 포인터의 크기를 뺀 만큼 데이터를 저장할 수 있다.)
✔ 장점
✔ 단점
➕ FAT (file allocation table)
Indexed Allocation (색인할당)
- 모든 포인터들을 하나의 장소 즉, 색인 블록으로 관리한다.
- 각 파일은 저장장치 블록 주소를 모아놓은 배열인 색인(index) 블록을 가지며, 색인 블록의 i번째 항목은 파일의 i번째 블록을 가리킨다.
- 파일이 생성될 때 인덱스 블록의 모든 포인터는 null로 설정된다.
✔ 장점
✔ 단점
그렇다고 색인 블록을 작게 만들자니,, 큰 파일에 대한 충분한 포인터를 보유할 수 없으니까 문제!
이를 해결할 3가지 기법을 소개한다.
새로운 파일을 만들려면 가용 공간 리스트를 탐색하여 새로운 파일을 위한 공간을 할당받아야 한다.
가용 공간 리스트는 흔히 비트맵, 또는 비트 벡터로서 구현된다.
각 블록은 1비트로 표현된다.
만약 블록이 비어 있으면 그 비트는 1이 되고 할당 되어 있으면 0이 된다.
모든 가용 블록을 함께 연결한다.
첫 번째 가용 블록에 대한 포인터는 별도의 장소에 보관하고, 각 블록은 다음 가용 블록을 가리키는 포인터를 가진다.
가용 리스트 방식의 변형으로, 첫 번째 가용 블록 내에 n개의 블록 주소를 저장하는 방법이다.
이 중 처음 n-1개는 실제로 비어있는 블록의 주소이고, 마지막 1개는 자신과 마찬가지로 n-1개의 빈 블록 주소를 가지고 있는 가용 블록을 가리킨다.
모든 블록을 일일이 추적할 필요 없이 연속된 가용 블록의 첫 번째 블록의 주소와 연속된 블록의 개수(count)만 유지하는 방식이다.
가용 공간 리스트의 각 항은 하나의 장치 주소와 블록의 개수로 구성된다.