파일 시스템

Overview

운영체제가 제공하는 서비스 중 ‘파일 시스템’이 존재한다.

사용자는 이 파일 시스템을 통해서 실제 데이터가 저장되어 있는 ‘파일’에 액세스할 수 있는 것
  • 파일 관리

    • 파일 저장, 참조, 생성, 삭제, 수정, 보호 등의 기능을 제공, 다른 사람의 파일을 공유 가능

  • 파일 구성(구조화)

  • 파일 액세스, 제어 관리

    • 저장 된 파일에 대해 기록, 실행 등 여러 종류의 액세스 제어 방법을 제공

  • 디스크 저장소 관리

    • 보조 메모리 관리 - 2차 저장 장치에 파일을 저장 할 공간을 할당

  • 파일 → 저장 단위, 논리적 보조 저장 장치의 가장 작은 할당 단위 == 실제 데이터를 저장한다!

  • 저장 장치의 물리적 특성을 고려해서 **논리적인 저장 단위인 ‘파일’**을 정의하고 물리 장치에 적재하는 것!

  • 파일은 일반적으로 프로그램과 자료로 나누어진다.

    • 프로그램: 소스 프로그램, 목적 프로그램으로 나누어짐.

    • 자료 : 숫자, 문자, 이미지, 소리, 이진수, 정해진 형식, 자유 형식 등 저장 가능한 모든 것이 자료가 됨.

파일 시스템 구조는,,✨

  • 파일이 무엇인지, 속성, 디렉터리 구조, 파일에 허용하는 연산 등을 정의 하는 논리적 파일

  • 실제 디스크에 이러한 논리 파일 시스템을 mapping 하는 것

으로 구분 할 수 있다!


파일

  • 블록

    • 파일은 디스크의 하나 이상의 블록에 저장된다. (파일이 읽혀 들어가는 곳)

    • 블록은 메모리 ↔ 디스크 간의 전송 단위

    • 0 ~ MAX 연속 정수인 가상 블록 번호를 통해 블록에 접근 가능

    • 이런 가상 블록 번호는 물리적 디스크 주소로 변환된다. (변환은 장치 드라이버가 수행)

    • Ex) VBN(가상 블록 번호) == 논리적 파일을 파일 시스템(OS)에 의해 LBN(논리 블록 번호) == 물리적 파일로 맵핑하는 과정

    • 0 (VBN) → 4 (LBN, 디스크 주소)

파일 구성

  • 파일의 내용(데이터, 논리적 파일)은 OS에 의해 실제 물리적 저장 장치로 맵핑(Mapping) 된다.

  • 파일 구성 요소 ✨

    • 항목(필드)

      • 데이터 파일을 구성하는 기본적인 구성 요소 → 의미 있는 데이터의 최소 단위

      • 필드 길이는 미리 정해지는 것이 보통이나 고정, 가변 길이 모두 가질 수 있음

        • 필드에 입력한 데이터가 할당 된 데이터 길이보다 짧으면 필드 나머지 부분은 빈공간으로 남음

      • 고정 길이 필드는 포함할 문자나 바이트의 수가 미리 결정된 필드!

      • 가변 길이 필드는 입력하는 데이터의 크기에 맞도록 최대 길이까지 확장됨

    • 레코드

      • 파일을 구성하는 요소 (필드 = 레코드를 구성하는 요소)

      • 하나의 파일에는 여러 레코드가 들어있음

      • 파일을 구성하는 레코드 몇 개가 모여 하나의 블록을 만들 수 있음

      • 레코드도 고정 길이 또는 가변 길이로 구성

        • 고정 길이 레코드 : 직접 접근이 쉽지만 레코드의 크기가 너무 작으면 문자열들이 잘려나가고, 너무 크면 저장공간이 낭비됨.

        • 가변 길이 레코드 : 잘리거나 낭비되는 문제는 해결하지만 레코드의 정확한 위치를 파악하기 어려워서 직접 접근이 어려움. 순차 접근에 적합

파일 속성

  • 파일은 ‘이름’을 가지고, 하나의 문자열로 나타냄 → ex) 메모장을 켜서 텍스트 파일을 저장 가능 Text.txt

  • 파일이 만들어지면 해당 프로세스, 시스템, 사용자로부터 독립하게 되는데 무슨말이냐면,,

    • 사용자가 파일을 만들면 → 파일을 복사하거나 e-mail로 첨부해서 보낼 수 있음 → 다른 사용자가 또 공유 가능

  • 파일 속성 (메타 데이터)

    • 이름 : 우리가 읽을 수 있는 정보

    • 식별자 : 파일 시스템 내에서 파일을 식별하는 고유 번호 (primary key)

    • 타입 : 여러 타입의 파일을 제공하는 시스템에서 필요

    • 위치 : 파일의 위치를 가리키는 포인터

    • 크기 : 파일의 크기

    • 소유자 : 파일 최초 생성자

    • 보호 : 접근 제어 정보 (읽기, 쓰기, 실행 등 접근 제어에 대한 정보를 나타남)

    • 시간, 날짜, 사용자 식별 : 생성일, 최근 변경일, 최근 사용일 등에 대한 정보

  • 파일 타입

파일 형태확장자기능

실행 가능

exe, com, bin

이진 수행 기능 프로그램

소스 코드

c, a, p, pas, asm, f77

다양한 언어 소스 코드

배치

bat, sh

명령어 해석기에 대한 명령

문서

txt, doc

텍스트 데이터

워드 프로세서

wod, hwp, doc

워드 프로세서 형식

라이브러리

lib, a, DLL

라이브러리 루틴

백업, 보관

arc, zip, tar

관련된 파일을 나로 묶어서 보관, 저장을 위한 압축

  • 모든 파일에 대한 정보는 보조 저장장치에 상주하는 디렉토리 구조 내에 유지된다.

  • 파일과 디렉터리 모두 비휘발적 성질을 가져야 하므로, 저장 장치 상에 저장되고, 필요할 때 조금씩 메모리로 가져와야 한다.


파일 연산

운영체제는 파일을 관리하기 위한 시스템 콜을 제공한다.
  1. 파일 생성

    • 1단계) 파일 시스템에 사용할 수 있는 공간이 있는지 발견해야 함

    • 2단계) 생성한 파일을 디렉토리 내에 만들어 파일의 이름과 파일 시스템 내의 위치를 기록해야 함

  2. 파일 쓰기 (기록)

    • 1단계) 파일의 이름과 기록될 정보를 명시하는 시스템 콜을 호출

    • 2단계) 파일의 이름을 가져오면 파일의 위치를 알기 위해 디렉토리를 탐색함.

    • 이 때, 파일의 쓰기가 일어날 위치를 가리키는 **쓰기 포인터(Write Pointer)**에 대한 정보를 함께 저장한다 → 이 포인터로 다음 블록의 주소를 계산할 수 있고 쓰기가 일어날 때마다 포인터는 갱신된다.

  3. 파일 읽기 (판독)

    • 파일의 이름과 파일이 읽혀 들어갈 블록의 위치를 명시하는 시스템 콜을 호출

    • 이 동안 시스템은 읽기가 일어 날 위치를 가리키는 **읽기 포인터(Read Pointer)**를 유지하고 있어야 한다.

    파일은 쓰거나 읽혀지므로 두 개의 포인터가 존재하고,
    프로세스는 일반적으로 파일 읽기나 쓰기 중 한 가지를 할 수 있는 ✨현재 파일 위치 포인터✨를 가짐.
    (해당 프로세스가 파일을 오픈한 가장 최근의 위치겠죠?)
    
    읽기/쓰기 포인터는 file descriptor(fd)마다 하나씩 존재하며 시스템에 의해 관리된다.
    
    포인터를 사용해서 공간을 절약하고 복잡성을 감소시킬 수 있다!
    • Q. 서로 다른 프로그램이 동일한 파일을 읽거나 쓰면 포인터는 어떻게 될까요? ✨

      서로 다른 프로그램이 동일한 파일을 동일한 방법으로 개방했다해도 file descriptor가 다르기 때문에 읽기/쓰기 포인터 역시 개별적으로 관리된다.

      즉, 하나의 프로그램이 읽기/쓰기 작업을 수행하여 포인터의 위치가 변경되더라도 다른 프로그램의 읽기/쓰기 포인터에는 전혀 영향을 주지 않는다.

  4. 파일 재설정 == 파일탐색

    • 디렉토리를 탐색해서 현재 파일의 포인터를 조정

    • 현재 파일의 위치 → 주어진 값 (새로운 위치) 파일의 첫 부분으로 재설정(reposition) 한다

  5. 파일 삭제

    • 지명된 파일을 디렉토리에서 찾음

    • 발견하면 파일이 차지하는 모든 공간을 해제 시키고 디렉토리 항목을 삭제(무효화) 시킴

    • 해당 공간은 다른 파일이 재사용 가능함!

    • 이렇게 파일 공간이 할당되고 해제되다 보면 단편화가 발생하지 않을까? ✨

  6. 파일 크기 조절

    • 파일의 크기를 변경

  7. 파일 절단

    • 파일 내용을 지우고 속성만 남김

  8. 파일 첨가(appending)

    • 파일에 새로운 정보 첨가

  9. 파일 재명명(renaming)


파일 접근✨

파일의 구성과 파일에 지원하는 연산이 뭔지 알았다.
그래서 어떻게 파일에 접근하는데??

✨ 파일은 디스크에 저장되고 → 이 파일에 접근해서 해당 정보가 메모리에 불러와서 우리가 참조할 수 있는 것.

즉, 접근(access) == 메모리에 저장된 데이터를 가져오는 과정

순차 접근

  • 파일의 정보는 레코드 단위 순서로 처리 되는 것이 가장 일반적

  • 읽기와 쓰기를 예로 들어보자면,,

    • 읽기 - 파일의 다음 부분을 읽은 후 자동으로 파일 포인터를 증가시킴

    • 쓰기 - 기록된 내용의 끝(파일의 새로운 끝)으로 포인터를 이동시킴

  • 이렇게 1개 레코드 단위로 앞뒤로 이동할 수 있는 것! (뒤로 돌아가기 위해서는 되감기를 해야 함)

  • 테이프 모델 기반

직접 접근

  • 모든 블록은 직접 읽거나 쓸 수 있고(Random Access 허용) 읽기나 쓰기의 특별한 순서가 없음 → 빠르게 레코드를 읽고 쓸 수 있는게 장점

    • ex) 블록 14 읽고 → 블록 53 읽고 → 블록 7을 쓸 수 있음

  • 대규모 데이터베이스에 직접 접근이 아주 유용

    • 어떤 Query가 들어오면 어떤 블록이 그 답을 가지고 있는지 계산하고 반환하기 위해 직접 정보를 읽어 들임

  • 디스크 모델 기반

  • ex) 현재 위치를 가리키는 변수인 cp만 유지된다면, 직접 접근 파일을 가지고도 순차 파일 기능을 쉽게 구현 가능

기타 접근

  • 인덱스 순차 접근 방법

  • 직접 접근 방법을 기반으로 인덱스를 구성해서 탐색하고, 포인터를 사용해서 파일에 접근하는 방법

  • 크기가 큰 파일을 적은 입출력으로 탐색할 수 있다는 장점!


디렉토리

많은 파일을 어떻게 관리하는 걸까?

  • 파일과 파일을 계층적으로 연결하는 디렉토리’를 사용하여 파일을 관리할 수 있다.

  • 파일의 이름과 위치를 담고 있으므로 파일이 어디 있는지 알고 접근 가능하다!

  • 디렉토리를 설계하려면,,

→ 사용자 수, 사용자 당 평균 파일 수와 크기 등 사용자들에 관한 지식 + 해당 시스템에서 실행할 응용 프로그램의 특성을 고려하여 적합한 디렉토리 구조(파일 구성)를 결정해야 한다.

  • 일반적으로 시스템은 두개의 분리된 디렉토리를 가지는데,,

  • 장치 디렉토리

    • 실제 장치에 저장되어 있으며, 장치에 있는 파일들의 물리적 속성 (파일 위치, 파일 크기, 파일 할당 과정 등)을 나타낸다.

  • 파일 디렉토리

    • 모든 파일의 논리적인 구성 (이름, 유형, 소유자(사용자), 계정 정보, 보호 접근 코드 등)을 기술하고 있는 디렉토리

  • 초기에는 단일 사용자를 위해 장치 디렉토리 만으로도 충분했다

→ but, 저장 공간 및 사용자 수가 엄청나게 증가하면 파일에 대한 구성과 추적이 어려워질것!

  • 그래서 빠르게 파일에 접근하기 위해 다른 디렉토리 구조를 추가 한 것 → 파일 디렉토리


디렉토리 연산

디렉토리에서 파일들은 Symbolic Name(기호로 된 이름)을 가지고 있다!

  • 파일 탐색

    • 이름을 가지고 특정 파일을 찾을 수 있다.

  • 파일 생성

    • 새로운 파일을 생성하여 디렉토리에 추가한다.

  • 파일 삭제

    • 디렉토리에서 파일을 제거한다.

  • 디렉토리 나열

    • 디렉토리 내부에 존재하는 파일들을 나열하고 내용을 보여준다.

  • 파일 재명명

    • 파일의 이름을 변경한다.

  • 파일 시스템 순회

    • 파일 시스템의 여러 디렉토리를 순회하며 파일들을 볼 수 있다.

  • 파일 백업

    • 일정 시간마다 주기적으로 파일 시스템의 내용과 구조를 복사해둔다.


디렉토리 구조

디렉토리는 조직화 되어 있기 때문에 순회 및 조회, 탐색 등이 가능할 수 있다!

디렉토리 개별 구조 (구현)

  1. 선형 리스트

  • 간단히 디렉토리를 구현하는 방법

  • 디렉토리에 파일 이름, 포인터 들의 선형적 리스트를 구성할 수 있음

  • But, 파일을 찾기 위해 선형 탐색을 해야하므로 오버헤드 발생,,!!

    • 그래서 OS는 사용된 디렉토리 정보를 저장하는 캐시를 구현해서 정보를 매번 디스크로부터 읽어 오는 것을 피한다고 한다 (캐시 적중)

    • or 리스트를 정렬해서 이진 탐색 기법을 이용 → But, 리스트가 정렬상태를 매번 유지하면 파일 생성 및 삭제가 복잡해질 수 있음(계속 정보가 이동하니깐)

    • 이것은 ‘연결 이진 트리’를 사용해서 해결할 수 있다

  1. 해시 테이블

  • 파일 이름을 제시 → 해시로부터 값(포인터)를 얻어 리스트에 직접 접근

  • 디렉토리 탐색시간을 줄이고 성능을 개선시킬 수 있어서 많이 사용 → 생성과 삭제가 쉽다

  • But, 둘 이상의 파일명이 같은 위치를 지정하는 경우는 충돌이 날수도,,

    • 충돌 해결 방법으로 ‘체인 오버플로우 해시테이블’을 사용

    • 각 해시 항목이 하나의 값이 아니라 연결 리스트가 됨

    • 새로운 항목은 이 연결리스트에 추가

    • 이름을 찾으려면 연결 리스트를 찾아보면 됨 (그래도 선형 탐색보다 훨씬 빠르다!)


디렉토리 전체 구조

1단계 디렉토리

  • 가장 간단한 구조의 디렉토리 (ex. 장치 디렉토리)

  • 모든 파일이 같은 디렉토리에 위치하게 된다. (유지가 쉬움)

  • But, 파일 수가 증가하거나 다수의 사용자가 존재한다면???

  • 또 같은 디렉토리 내에 있으므로 모두 고유한 이름을 가져야 하는 불편함이 존재,, (사용자가 한 사람이더라도 고유한 이름으로 새 파일을 생성해야 하므로 파일명을 일일히 기억해야 함,,shit)

2단계 디렉토리

  • 위에서 발생하는 파일명 혼란 예방을 위해

  • → 각 사용자들에게 개별적인 디렉토리를 만들어준다!

  • UFD(User File Directory)

    • 자신만의 사용자 파일 디렉토리

    • 각 UFD는 유사한 구조(선형, 이진 혹은 해시)를 가지며 오직 한 사용자의 파일들만을 나타냄✨

    • 따라서 파일 이름이 충돌하는 문제 해결 + 다른 사용자 접근할 수 없음 (장점)

    • But, 두 사용자가 한 파일을 공유해서 사용해야 하는 경우는,,? (서로가 자신의 UFD 액세스를 허용해주지 않으면 공유 불가능, 액세스를 허용해도 다른 UFD 내부의 파일 이름들을 알아야 함,,)

  • MFD(Master File Directory)

    • 한 사용자의 업무를 시작하거나 새로 로그인(등록) 할 때는

    • → 각 사용자의 이름 또는 계정 번호 & 각 UFD에 접근할 수 있는 포인터가 존재하는 MFD를 먼저 탐색해서 UFD를 찾아낸다!

  • 사용자 1이 특정한 a 라는 파일을 참조할 때, 자신의 UFD만 탐색하므로 여러 사용자 사이에서 a 라는 같은 파일명을 가질 수 있는것! (운영체제가 a라는 파일을 생성 or 삭제 할 때도 마찬가지)

2단계 디렉토리에서의 경로명?

  • root == 마스터 파일 디렉토리 (MFD)

    • 그 아래 사용자 파일 디렉토리 (UFD)

      • 그 아래 파일 == leaf

**root로부터 - leaf(파일)까지의 경로**를 정의할 수 있음 
→ 시스템 내의 모든 파일은 ‘유일한 경로명’을 가진다!
  • 위에 그림에서 내가 사용자1 일때 test 파일에 접근하고자 하면 단순히 ‘test’로 접근

  • But, 사용자3의 test 파일에 접근할 때는 (사용자 3의 디렉토리 이름 USER3)

    /USER3/test

로 접근 해줘야 함

→ TMI : 파일을 지명하기 위한 문법은 시스템마다 다르다!! ( 파티션(/)이 아닌 콜론(:)으로 나타내는 등)

트리 구조 디렉토리

  • 아까 2단계 트리구조는 height가 2인 트리구조였다.

  • 디렉터리 구조를 ‘트리 구조’로 확장할 수 있다는데,,

사용자들은 자신의 Sub Directory를 생성하고 이 곳에 자신의 파일을 구성할 수 있다!

트리 특징

  • 하나의 루트 디렉토리 가짐

  • 시스템 내의 모든 파일이 유일한 경로명을 가짐

  • 디렉토리(or 서브 디렉토리)는 서브 디렉토리 및 파일들을 가질 수 있음 → 모든 디렉토리들은 내부적으로 똑같은 형식으로 이루어짐!

    • 그래서 서브 디렉토리와 파일을 구분하기 위해 비트를 지정해서 구분

      • 서브 디렉토리 1

      • 파일 0

  • 트리 구조 디렉토리도 경로명으로 파일에 접근할 수 있는데 경로명에는,,

    • 절대 경로명 - 루트에서부터 목적 파일까지의 모든 경로를 지정하는 것

    • 상대 경로명 - 현재 디렉토리의 위치를 기준으로 목적 파일까지의 경로를 지정하는 것

  • 현재 디렉토리가 root/spell/mail 이라고 해보자. 파일 first에 대해서,,

    • 절대 경로명 → root/spell/mail/prt/first

    • 상대 경로명 → prt/first

  • 트리 구조에서 디렉토리 삭제 방법?

    • 디렉토리가 비어있다면 → 그냥 바로 삭제하면 됨!

    • 디렉토리가 비어있지 않으면 바로 삭제되지 않는다 → 먼저 디렉토리 내부의 모든 파일을 제거해줘야함

    • 아니면 디렉토리 삭제 요청이 들어오면 바로 디렉토리 내부의 모든 서브디렉토리 & 파일을 삭제해버릴수도 있지만 위험,,

  • 두가지 방법 중 필요에 따라 결정한다!


디스크 할당 방법✨

지금까지 파일의 구조와 속성, 파일에 접근하는 방법, 파일들을 관리하는 디렉토리 구조를 알았다!

그러면 이 파일을 디스크에 저장한다고 했는데,, 
  • OS는 어떻게 파일을 디스크 공간에 배치할까? ✨

  • 또 어떻게 공간을 사용하고, 어떻게 파일에 빨리 접근할 수 있을까? ✨

  • 디스크 공간 할당을 위해서 3가지 방법이 널리 사용된다 (일반적으로 시스템은 특정한 하나의 방법을 채택해서 사용한다)

  • 파일이 블록들로 구성되어 있다고 가정해보자.

1. 연속 할당

파일들을 디스크의 연속적인 주소들의 집합에 할당하는 방식 (페이징 때 연속 메모리 할당과 비슷하죠?)
  • 한 파일의 연속 할당 → 시작 주소블록의 개수에 의해 정의됨 → 무슨말이냐면,,

    • 파일이 n개의 블록을 갖고, b의 위치에서 시작한다고 해보자

    • 이 파일은 블록 b, b+1, b+2 ,,, b+(n-1) 개의 블록을 차지하게 된다.

    • 밑에 그림에서 count 파일은 0 블록에서 2개, tr 파일은 14 블록에서 3개로 연속 할당된다!

  • 연속 할당의 장점

    • 파일 액세스가 쉬워서 직접 접근이 가능하다!

      • 블록 b에서 시작되는 파일의 i번째 블록에 직접 접근하기 위해서 b+i 블록에 접근

    • 순차 접근을 할 때도 파일 시스템이 마지막으로 참조된 블록의 디스크 주소를 기억해서 다음 블록을 읽을 수 있음

    • 그래서 직접/순차 접근 모두 연속 할당 기법을 지원할 수 있는 것

    • But, 새로운 파일을 위해서 연속적인 빈 공간을 찾아내야 함,,

  • 연속 할당의 단점

    • 외부 단편화 발생

      • 파일들을 할당/삭제를 반복하면서 디스크 공간들이 조그만 조각들로 나누어짐 ✨ (위에서 잠깐 언급했었죠?)

      • 해결하는 방법은,, hint → 디스크를 비워버린다면?

    • 파일 공간 크기 결정

      • 얼마나 많은 공간이 필요할지 결정하는 것은 어렵다,,

      • 해결 방법은,,

        1. 공간이 부족하면 오류 메세지 출력하고 사용자에게 더 많은 공간 할당 요청을 받는 것 → 이때 사용자는 일반적으로 필요한 공간을 크게 추정하는데 결론적으로 공간적 낭비,,

        2. 아주 큰 공간을 찾아서 파일들을 새로운 공간에 복사를 하고 이전 공간을 해제 → 시간적으로 낭비

2. 연결 할당

각 파일들이 디스크 블록들의 '리스트'에 연결된다.
이 디스크 블록들은 디스크 내에 흩어져 있어도 된다 -> 이 말은 위치 값을 식별할 수 있는,, 포인터!!존재
  • 한 파일 당 첫 번째 & 마지막 블록의 포인터를 가지고 있음!

  • 뭔말이냐면,, (약간 페이징 때 불연속 할당 기법 생각나죠?)

  • 5개의 블록으로 구성된 파일 존재

  • 블록 9에서 시작 - 블록 25에서 끝 (디렉토리는 9, 25 → 2개의 포인터를 가지는 것✨)

  • 이제 내부에서 각 블록 하나마다 다음 블록의 포인터를 가짐!

  • 9 → 다음 16, 16 → 다음 1 ,,,, 25 → -1 까지 연결!!

  • 연결 할당 장점

    • 파일 생성이 쉬움 → 그냥 바로 디렉토리 내에 생성하면 됨

  • 연결 할당에는 외부 단편화가 있을까요? ✨

    모든 블록들이 함께연결되어 있어서, 빈공간 리스트들에 채워서 연결하면 됨~

  • 연결 할당 단점

    • 순차 접근만 효과적으로 사용 가능

      • 파일의 i번째 블록을 찾으려면 첫 포인터부터 시작해서 타고 타고,, 따라가야 함 → 직접 접근이 불가능

    • 포인터를 위한 공간 필요

      • 항상 각 파일마다 두 포인터를 위한 2 워드가 필요함

    • 포인터를 잃어버리거나 파괴되면,,, 곤란

    • 파일들이 흩어져 있어서 탐색시간 증가

  • 해결 방법은,, 파일 할당 테이블(FAT)를 이용해서 변형하기 (1 파일 : 1 FAT)

    • test 파일이 217 블록에서 시작된다고 하자

    • FAT에서 217을 찾아서 다음 618로 간다.

    • 618 → 339로 타고타고 가다가 EOF 끝 값을 가지고 있는 마지막 블록까지 계속될 것이다.

    • 이 파일은 217, 618, 339 블록으로 구성된 파일인 것

  • FAT는 MS-DOS 와 OS/2 운영체제 해서 채택한 방식

3. 인덱스 할당

모든 포인터들을 하나의 장소, 즉 인덱스 블록으로 관리해서 직접 접근을 지원하는 방법
  • 위에서 연결 할당이 연속 할당의 크기 결정 문제와 외부 단편화 문제를 해결,,

  • But, 순차 접근만 가능하고 포인터들이 흩어져 있는 문제 → 이걸 해결!

  • 디렉토리는 각 파일(항목)들의 인덱스 블록 주소를 가짐

  • 각 파일들은 자신의 인덱스 블록을 가짐 == 디스크 블록 주소의 배열 ! (아래 그림 참조)

  • 그래서 이 인덱스 블록으로 어떻게 하냐,,

    • i 번째 블록을 읽기 위해 i 번째 인덱스 블록을 찾아서 읽을 수 있다 → (페이지 ↔ 프레임 페이징 비슷하쥬?)

    • 순차 접근 보다는 직접 접근이 빠르다! (인덱스 블록으로 바로 접근 가능)

    • 파일이 생성 될 때 인덱스 블록의 모든 포인터 값은 nil 값으로 초기화

    • i 번째 블록이 처음 쓰여지면 → 이 블록의 주소가 i 번째 인덱스 블록에 기록되는 것

  • But,, 각 파일들은 하나의 인덱스 블록을 가져야만 하는데 → 공간 낭비

    • 그래도 파일의 크기가 대부분 작기 때문에 인덱스 블록의 크기도 가능한 작을수록 좋다.

    • 그러나 인덱스 블록이 작아질 수록, 큰 파일에 대해서 충분한 포인터를 가질 수 없음

  • 디렉토리는 jeep 파일을 할당하기 위해 인덱스 블록을 본다 → 19로 인덱스 블록 찾음

  • 인덱스 블록에 쓰여있는 주소에 할당!

정리하면,,

  • 연속 할당

    • 디스크 블록을 얻기 위해 1번만 액세스 하면 됨 (첫주소 가서 연속으로 읽으면 되니깐)

    • 파일 직접 + 순차 접근 모두 지원

    • 작은 파일들에 효율적, 평균 성능도 아주 좋다고 함

  • 연결 할당

    • 순차 접근은 좋으나, 직접 접근의 경우 i 번째 블록을 읽기 위해 타고타고,, i 번만큼 디스크 읽어야 하는 경우 존재

  • 인덱스 할당

    • 인덱스 블록이 메모리 내에 있을 때 → 디스크 블록에 직접 접근 가능

    • But, 메모리 내에 인덱스 블록을 보관하는 것은 메모리 효율 떨어짐,,

    • 그래서 인덱스 블록이 밖에 있으면 →

      • 인덱스 블록을 읽고 → 데이터 블록을 읽을 수 있음

      • 만약 5번째 인덱스이면 5번이나 읽어야 함

    • 인덱스 구조 및 파일의 크기와 원하는 블록의 위치에 따라 성능이 좌우됨,,

  • 그래서 작은 파일들 (3-4개 블록을 가짐) 은 연속할당을 사용하고, 파일이 커지면 자동 인덱스 할당으로 변환하는 방식을 사용하기도 한다고 함!

Q. 이렇게 디스크에 파일을 할당할 때, 디스크 공간은 제한되어 있기 때문에 삭제 된 공간을 다시 
재활용 할 필요가 있다 -> 빈공간들을 관리해야 하는 것

디스크의 빈 공간을 관리하는 효율적인 방법?

파일 시스템을 어떻게 제공할 것인가

  • 그냥 저장 장소인 disk를 어떻게 디렉토리라는 추상화를 제공할 것인가?

  • 디스크를 블럭으로 나누고, 데이터를 저장한다.

    • 이 때 대부분의 OS의 파일시스템에서 블럭 크기는 4KB인데, 페이지 크기와 맞추기 위함이다.

  • 나눠진 블럭 중에서 데이터가 저장될 데이터 영역을 정해둔다.

Inode

  • 데이터가 어디 저장됐는 지 알기 위해선 파일 별로 정보를 저장해야한다. 이 때 어떤 데이터 블럭들이 파일을 구성하고, 크기는 몇이고, 권한과 소유자, 접근했던 시기 등의 모든 메타 정보가 inode(index node)에 저장된다.

  • inode역시 블럭에 inode table이 저장되어 있을 것이다.

  • inode는 고정된 direct pointer와 하나의 indirect pointer를 갖는다. direct pointer는 데이터 블럭이 어디에 있는지 직접 번호를 가리키고, 데이터 블럭이 많아져서 direct pointer만으로 표현이 불가능하면 indirect pointer가 또 다른 포인터를 가리켜서 해당하는 곳에 데이터 블럭이 위치한 곳을 나타낸다.

  • 다만 inode를 통해 데이터 블럭을 찾기 전에 파일의 inode가 어디에 있는지를 먼저 알아야할 것이다. 그럼 inode number는 어디에 있을까?

Directory

  • 디렉토리 역시 파일이다. 다만 특별한 파일인 것이다. → 즉 디렉토리도 inode가 있다.

  • 파일 이름과 inumber 쌍이 저장되어 있다.

Free list

  • inode는 파일마다 있어야할 것이다. 그럼 이런 inode가 저장되어 있는 inode block도 있을 것이다. 그렇다면 새로운 파일이 생성된다면? 우선 inode를 저장야하므로 inode를 위한 빈공간을 할당받고, inode가 가리킬 데이터를 위한 데이터 블럭 역시 할당받아야 한다.

    • 이 때 데이터 블럭을 할당받을 때, 연속적일 필요는 없지만 연속적이면 성능이 올라간다. 하드디스크의 헤드 섹터가 이동하는 시간이 있기 때문이다.

  • 따라서 비어있는 블럭에 대한 정보를 관리해야 한다. 그러므로 빈 inode block과, 빈 데이터 블럭에 대한 정보를 갖고 있는다.

Superblock

  • 파일 시스템마다 데이터 영역의 블럭이 몇개고, Inode가 저장되어 있는 블럭은 몇개고 등등의 전체 파일 시스템에 대한 정보 역시 어딘가에 저장이 되어 있어야 그것을 바탕으로 파일 시스템을 해석할 것이다.

  • 따라서 inode, data block 개수, inode table이 디스크에서 어디에 있는지 등의 정보를 super block에 저장한다.

  • file system이 mount될 때, OS가 superblock을 먼저 보고(그러기 위해선 슈퍼블록은 OS가 이해할 수 있는 format으로 약속되어 있어야 한다), 파일 시스템 트리에 저장장소를 mount한다.

Last updated