매체에 따라 순차적인 접근, 직접 접근 방식이 있었다. 하지만 데이터 관리 방법에 따라 순차접근만 가능한 경우도, 직접접근만 가능한 경우도 있다.
디스크에 파일의 데이터를 저장하는 방법은 세가지 방법으로 나눠볼 수 있다. 파일의 크기는 균일하지 않다. 디스크에 다가 파일을 저장할 때는 동일한 크기의 섹터 단위로 나눠서 저장한다. 파일 시스템이라던지 디스크 외부에서 볼 때는 동일한 크기로 저장한 단위를 논리적인 블럭이라고 한다. 즉, 어떤 임의의 크기의 파일을 동일크기의 블럭 단위로 나누어서 저장하고 있는데, 이것은 메모리 관리에서 페이징 기법과 유사하다.
디스크 상에 연속해서 저장되는 방법이다. 블럭 두개로 저장되는 것은 첫번째 블럭과 두번째 블럭으로 인접하게 저장된다. 디렉토리 파일은 디렉토리 밑에 있는 파일들의 메타데이터를 내용으로 한다고 했었다. 따라서 디렉토리 밑에 지금 파일의 이름들이 있고 메타 데이터도 start, length를 가지고 있는 것을 볼 수 있다. 연속적으로 저장되기 때문에 시작 위치와 길이를 가지고 있는것이다.
list라는 파일은 28번 부터 길이가 4로 연속해서 할당된 것을 알 수 있다. 이러한 할당 방법의 장점과 단점을 알아보자
파일의 데이터를 연속적으로 배치하지 않고 비어있는 곳이면 들어가도록 하는 것이다. start에서 시작 -> 9번에 가면 두번째 데이터가 어디있는지 적혀있다. 따라서 각각의 블럭에 다음 블럭이 어딘지 적혀있음
마지막 블럭에는 끝났다는 표시를 해놓는다.
시작 위치만 디렉토리가 가지고 있고 그 다음 위치는 파일에 가봐야 알 수 있다.
직접접근이 가능하게 하기 위해서 디렉토리에 파일의 위치 정보를 바로 저장하는 것이 아니라 인덱스를 가리키도록 한다. index 블록은 내용이 담여 있는 것이 아닌 위치 정보를 열거 해놓은 정보를 담아놓는다. 따라서 총 5개의 블럭으로 구성되는 것을 확인할 수 있다. 따라서 jeep 파일의 4번째 블록을 보고 싶으면 index 블록을 살펴 4번째 블록에 바로 접근할 수 있다.
외부조각이 생기지 않으면서 직접접근 가능
아무리 작은 파일이라 하더라도 블럭이 두개 필요하다.
index를 위한 블럭, 실제 데이터 블럭
따라서 small file의 경우 공간 낭비 -> 실제로 많은 file들이 small
굉장히 큰 파일의 경우 하나의 index 블럭으로 파일의 모든 index를 표현하지 못한다.
해결방안
파일을 디스크에 저장하는 방법 세가지에 대해 알아보았다. 이러한 방법들은 이론적으로 할당이 가능하다는 것이고, 실제 파일 시스템에서는 어떤 할당 방법을 어떻게 변형해서 사용하는지 알아본다.
파일 이름 | inode 번호
로 저장되어 있다.파일 이름 | inode 번호
)-FAT
파일의 메타데이터 중 일부(위치 정보)를 FAT에 보관, 나머지는 directory가 가지고 있는다.
심지어 파일의 시작 위치 또한 가지고 있다. Linked allocation에서 bad sector가 있을 때 데이터가 유실될 수 있고, 512 바이트를 모두 활용하지 못한다는 단점이 있었다. FAT은 이를 보완하기 위해 위치 정보를 file내에 저장하는 것이 아니라 FAT이라는 별도의 공간을 활용해 관리한다. FAT의 배열의 크기는 디스크가 관리하는 data block의 개수만큼이다. 배열에는 숫자를 하나 담을 수 있는데, 그 숫자는 해당 블럭의 다음 블럭이 어디 있는지 담고 있다. 따라서 처음에 217번을 보고 다음 블럭을 찾고 싶다면 FAT을 보면 된다. EOF는 파일의 끝을 나타내는 것이다.
Linked allocation을 적용했으나 FAT을 이용해 직접접근이 가능하다. 기존에는 순차적으로 파일들을 찾아 seek를 매번했어야했다. 하지만 FAT을 메모리에 올려놓고 쭉 따라가면 곧바로 해당 파일의 위치를 파악할 수 있다. 디스크에서 2번째, 3번째 파일을 봐야만 알 수 있는 것이 아니다.
Reliability 문제도 해결했는데, 포인터 하나가 유실되더라도 data block과 분리된 FAT에 위치 정보가 있다. 따라서 FAT은 굉장히 중요한 정보를 가지고 있으므로 보통 2copy 이상을 가지고 있어 reliability를 해결할 수 있다.
비어있는 블럭을 어떻게 관리할지 알아보자
각각의 블럭별로 번호가 있다. 따라서 각각의 번호에 bit를 통해 block이 사용중인지를 표시한다. 따라서 bit[i]
가 0이라면 i번째 블럭은 빈 블럭이고, bit[i]
가 1라면 i번째 블럭은 차있는것이다. bit map의 크기는 블럭의 크기와 같다.
비어있는 블럭들을 연결해놓는 것이다. 비어있는 블럭이기 때문에 다음에 비어있는 블럭이 어디인지 포인터를 저장할 수 있다. 따라서 다음 빈 블럭을 알기 위해선 해당 블럭을 봐야한다. bit map에 비해 추가적인 공간을 필요로 하지 않는다는 장점이 있으나, 연속적인 가용공간을 찾기 어렵다.
linked list방법의 변형
첫번째 free block이 n개의 pointer를 가짐
- n-1 pointer는 free data block을 가리킴
- 마지막 pointer가 가리키는 block은 또 다시 n pointer를 가짐
비어있는 블럭을 한번에 찾기는 효율적이나, 연속적인 빈 블럭을 찾기에는 효율적이지 못함
연속적인 빈 블럭들을 표시하기 위해서 첫번째 빈 블럭의 위치와 해당 위치 부터 연속적으로 빈 블럭의 수 또한 가지고 있다.
크기를 고정해놓고 관리
파일의 이름을 그냥 저장하는 것이 아닌 hash 함수를 적용해서 저장하는 것이다.
File의 meatadata의 보관위치
긴 파일 이름을 지원하는 방식
metadata의 크기는 한정적이다. 하지만 file의 이름은 그렇지 않다. 몇 byte로 제한하는 등의 방법을 사용할 수 없기 때문이다.
파일시스템의 종류가 많다. 사용자가 파일시스템을 접근할 땐 system call을 해야한다. 이때 file system 별로 다른 system call interface를 사용해야 한다면 사용자가 굉장히 혼란스러울 것이다. 그래서 보통은 어떠한 파일 시스템이 실제로 사용되던 상관없이 개별 파일 시스템 윗 계층에 virtual file system interface를 둔다. 그래서 사용자가 파일 시스템에 접근할 때는 파일시스템의 종류와 상관없이 VFS를 사용하는 것이다.
client, server 컴퓨터가 network로 연결되어있다. client가 VFS를 통해 file system에 접근한다. 이때 접근하는 것이 로컬 스토리지에 있는 것 뿐만아니라 원격 저장소에 있는 것에도 접근할 수 있는 것이다.
client가 요청 -> 내 로컬 스토리지에 있는 것이 아님 -> NFS client 발동 -> 네트워크를 통해 server 연결 -> VFS를 통해 접근
NFS를 지원하려면 server, client쪽에 모두 NFS 모듈이 있어야하고 같은 약속을 해야한다.
파일의 데이터를 사용자가 요청했을 때 디스크에서 읽어 사용자에게 전달하기만 하는 것이 아니고, 운영체제에 저장해 다른 사용자가 요청했을 때 운영체제에 저장되어있는 데이터를 전달해주는 것
메모리에 있던 디스크에 있던 CPU 제어권이 운영체제에게 넘어가기 때문에 LRU 등을 사용할 수 있다.
기존의 방식
read/write system call -> 운영체제가 파일 시스템에 있는 내용을 자신의 buffer cache로 옮겨옴 -> 사용자에게 전달 -> 자신의 주소 영역 중 page에 copy해서 사용
memory mapped I/O -> system call -> 자신의 주소 공간 중 일부를 file에 mapping -> mapping을 해도 디스크의 파일을 읽어오는 것은 동일 -> 해당 내용을 page cache에 copy -> 지금부터는 사용자 프로그램이 자신의 page cache에 memory에 읽고 쓰듯이 사용
mapping만 해놓고 memory에 올리지 않으면 page fault가 남 -> 운영체제로 CPU가 넘어감
이미 만약 mapped 된 영역이 주소공간에 올라와있으면 커널없이 사용
둘의 진행방향은 같으나, 운영체제의 도움을 얼마나 받는지 다르다. memory-mapped은 운영체제를 부르지 않고 자신의 주소영역에서 사용하는 것이 장점이다. 어떻게 하던 buffer cache를 거쳐야 함
공간을 따로 나누어놓지 않고 필요에 따라 할당해서 사용
경로가 더 단순해짐
read/write -> system call -> 운영체제로 CPU 제어권이 넘어감 -> 메모리에 올라와있는 데이터는 copy, 올라와있지 않다면 디스크에서 읽어와 copy 해 전달
memory mapped -> 자신의 주소 영역 중 일부를 file system에 maping -> 사용자 프로그램의 주소영역에 page cache 매핑 -> 한번 더 copy해서 올라가는 것이 아닌 page가 주소영역에 mapping됨
프로그램이 file system에 실행파일 형태로 저장되어 있다가 실행파일을 실행시키면 프로세스가 된다. 프로세스가 되면 프로세스만의 독자적인 주소 공간인 virtual memory가 만들어진다. 주소 변환 HDD 에 의해 virtual 메모리 중 당장 필요한 것은 물리적인 메모리에 올라가고 나머지는 디스크의 swap area에 있는다.
여기서 간과한 것이 code 부분이다. code부분은 메모리에 올라간다음 쫓겨날 때 swap area로 내려가지 않는다. code는 read-only이기 때문에 file system의 실행파일에 저장되어있으므로 swap area에 저장할 필요가 없다.
memory mapped I/O 를 사용하는 대표적인 예제가 실행파일을 실행시켜 생긴 virtual memory의 code 부분이다. file systme의 파일 내용이 그대로 프로세스의 주소 영역에 mapping된 것이다. 만약 이 프로그램이 특정 코드로 접근하는데 메모리에 없다면 swap area에서 올리는 것이 아닌 file system에서 올려 사용한다.
실행 파일 뿐만 아니라 데이터 파일도 file system에 저장되어있다.
B프로그램이 실행되다가 데이터 파일을 memory mapped 형태로 사용하고 싶다면 운영체제에게 해당 데이터 파일을 해당 프로그램의 주소공간에 mapping해달라 요청한다. 운영체제는 mapping해주고 프로그램이 실행되면서 mapping된 곳에 접근할 때 안올라와있다면 page fault가 나 운영체제가 file system에서 읽어와 memory로 올리는 것이다. 이 이후부터 접근할 때는 OS의 도움을 받지 않고 스스로 사용한다. 하지만 이 내용이 쫓겨날 땐 swap area로 쫓겨나는 것이 아닌 file system 에 변경 내용을 쓰고 쫓겨난다.
read/write system call을 통해 사용할 수도 있음 -> memory의 내용을 copy해서 전달
memroy mapped -> memory의 내용을 주소공간에 mapping해서 사용
Operating System Concepts 10th
KOCW 강의 - [운영체제] 이화여자대학교 반효경 교수