파일 시스템과 RAID

junto·2024년 3월 19일
0

operating system

목록 보기
12/13
post-thumbnail

대부분의 시스템에서 운영체제는 파일의 구조를 모른다. 운영체제는 어떤 저장 장치에 해당하는지, 어떤 파일 형식인지에 구애받지 않고 그저 데이터를 디스크에 안전하게 저장하고 되돌려준다. 이는 운영체제가 다양한 저장 장치를 파일(file)이라는 동일한 논리적 단위로 보기 때문이다. UNIX 파일 시스템을 이해하기 위한 다양한 용어를 살펴보고, 추가로 RAID 시스템이 무엇인지 알아본다.

1.파일과 파일 시스템

1) 용어 정리

(1) 파일(file)

  • 파일은 단순히 읽거나 쓸 수 있는 순차적인 바이트 배열이다. 각 파일은 저수준의 이름(아이노드 번호)를 가지고 있다. 비휘발성 보조기억장치에 저장된다.

(2) 디렉터리(directory)

  • 파일의 메타데이터 중 일부를 보관하고 있는 일종의 특별한 파일이다. 파일을 검색, 생성, 삭제, 목록 조회, 이름 변경, 파일 시스템 내부 조회 등 다양한 연산을 지원한다.
  • 디렉터리에 파일이 있는지 찾기 위해 해시 테이블을 이용하면 효율적으로 찾을 수 있다.

(3) 메타데이터(metadata)

  • 파일 자체의 내용이 아니라 파일을 관리하기 위한 각종 정보들이 저장된다. 파일 이름, 유형, 저장된 위치, 파일 사이즈, 접근 권한(읽기/쓰기/실행), 시간(생성/변경/사용), 소유자 같은 정보가 저장된다.
  • 디렉터리에다 파일의 메타데이터를 저장할 수도 있겠지만 다른 곳에 별도로 저장할 수도 있다. (Inode, FAT)
  • 아주 긴 파일 이름의 경우 file name이 고정 크기보다 길어질 때 entry의 마지막 부분에 이름의 뒷부분이 위치한 곳의 포인터를 둘 수 있다.

(4) 파일시스템(file system)

  • 운영체제에서 파일을 관리하는 부분을 말한다. 파일 및 파일의 메타데이터와 디렉터리 정보 등을 관리한다. 파일의 저장 방법, 파일 보호 방법 등 여러 기능을 제공한다.

(5) physical formatting

  • 디스크를 컨트롤러가 읽고 쓸 수 있도록 섹터들로 나누는 과정을 말한다. 각 섹터는 header + 실제 data(보통 512byte) + trailer로 구성된다.
  • header와 trailer는 sector number, error-correcting code 등의 정보가 저장되며 controller 직접 접근하여 읽고 쓴다.

현대에는 데이터 저장 방식의 효율성을 높이기 위해 더 큰 공간을 사용하며, SSD 경우에는 섹터가 아닌 페이지와 블록 단위로 관리된다.

(6) partioning

  • 디스크를 하나 이상의 실린더 그룹으로 나누는 과정을 말한다. OS는 이러한 파티션을 독립적인 disk로 취급한다. 이러한 디스크를 logical disk라고 한다.
  • 하나의 디스크 안에 여러 파티션을 둘 수 있으며, 여러 개의 물리적인 디스크를 하나의 파티션으로 구성할 수도 있다.

(7) booting

  • 컴퓨터가 켜지면 기본 입출력 시스템(BIOS) 또는 UEFI와 같은 펌웨어가 ROM(읽기 전용 메모리)에서 실행된다. BIOS 또는 UEFI는 저장 장치(예: 하드 디스크 드라이브나 SSD)의 섹터 0에 위치한 부트 로더를 찾아 실행한다. 이 부트 로더는 운영 체제 로더를 찾아 메모리에 로드하여 운영체제가 실행될 수 있게 한다.

2) 파일시스템 인터페이스

(1) open

  • 디스크로부터 특정 파일의 메타데이터를 메모리로 가지고 오는 시스템 콜이다.
  • open("/a/b/c")를 호출했을 때 동작 과정
    1. 루트 디렉터리 "/"를 open하고 그 안에서 파일 "a"의 위치를 얻는다.
    2. 파일 "a"를 open한 후 read하여 그 안에서 파일 "b"의 위치를 얻는다.
    3. 파일 "b"를 open한 후 read하여 그 안에서 파일 "c"의 위치를 얻는다.
    4. 파일 "c"를 open한다.
  • 그림으로 나타내면 아래와 같다.
  • 프로세스마다 별도의 file destriptor table을 갖는다. open 시스템 콜을 통해 특정 파일의 메타데이터를 올려둠으로 여러 프로세스가 공유해서 사용할 수 있다. 이를 버퍼 캐시(buffer cache)라고 한다. open file table은 전역적으로 하나만 존재한다. 별도의 테이블에 offset을 두어 파일에 어느 위치에 접근하는지 표시한다.
  • O_CREATE, O_WRONLY, O_TRUNC 등 플래그를 생성하여 파일의 생성, 권한 부여, 기존 파일 삭제를 할 수 있다.
  • open은 파일디스크립터를 반환한다. 파일디스크립터는 프로세스마다 존재하는 정수로 UNIX 시스템에서 파일을 읽고 쓰는 데 사용된다. 기본적으로 프로세스는 표준 입력, 표준 출력, 표준 에러를 위한 3개의 파일디스크립터를 열어둔다.

최근 파일시스템은 buffer cache를 별도의 블록으로 두지 않고, 페이지 단위로 관리한다. (Unified Buffer Cache)

(2) read()

  • read()의 첫 번째 인자는 파일 디스크립터로 파일 시스템에서 어떤 파일을 읽을 것인지 알려준다. 이는 순차적으로 파일을 읽어 들인다.

(3) lseek()

  • 파일 임의의 오프셋에서 읽기를 수행할 수 있다. 즉 비순차적인 접근이 가능하다.
  • lseek()을 호출한다고 해서 디스크를 탐색하는 것은 아니다. 파일 임의의 부분을 읽고 쓸 때는 실제로 많은 디스크 탐색이 발생하지만, 그렇다고 실제로 I/O 발생하는 건 아니다. 오프셋만 변경하기 때문이다.

(4) write(), fsync()

  • write()는 저장 장치에 기록해달라고 파일 시스템에 요청하는 것이다. 성능상 파일 시스템은 쓰기들을 일정한 시간(예를 들어 5초~30초)동안 메모리에 모은다. 응용 프로그램 입장에서는 쓰기가 완료된 것처럼 보이지만, 실제로 저장 장치에 저장된 것은 아니다.
  • 프로세스가 특정 파일 디스크립터에게 fsync()를 호출하면 파일 시스템은 지정된 파일의 모든 갱신된(dirty) 데이터를 디스크로 강제로 내려보낸다.

(5) rename()

  • mv로 파일의 이름을 변경한다. 시스템 크래시에 대해 원자적으로 구현되었다.

(6) stat(), fstat()

  • 파일에 대한 메타데이터를 보기 위해선 stat(), fstat() 시스템 콜을 호출한다.
  • link() 시스템 콜은 하드 링크로, 두 개의 인자를 받는데 하나는 원래의 경로명이고 다른 하나는 새로운 경로명이다. 파일은 복사되지 않고 대신 같은 파일을 가리키는 파일이 생성된다. 즉 참조가 하나 늘어난다.
    • 하드 링크는 제한이 많다. 디렉터리에 적용할 수 없으며 다른 파일시스템에서도 하드링크로 연결할 수 없다. -s 플래그를 전달하면 소프트 링크를 사용할 수 있다.
  • unlink()는 지워야 하는 파일의 이름을 인자로 받은 후에 성공하면 0을 리턴한다. 파일을 unlink하면 참조 횟수를 검사해서 참조 횟수가 0에 도달하면 파일 시스템은 비로소 아이노드와 관련된 데이터 블록을 해제하여 파일을 진정으로 삭제한다.

(8) mmap()

  • File의 일부를 virtual memory에 mapping 시키는 방법이다. 한 번 매핑하면 시스템 콜을 사용하지 않고 메모리를 읽고 쓸 수 있다.

(8) mount()

  • 파일 시스템을 저장 장치와 연결하는 시스템 콜을 mount()라고 한다.
  • 특정 디렉터리 파일 이름에다가 또 다른 파티션에 있는 파일 시스템을 마운트하게 되면 특정 디렉터리 파일 이름에 접근할 때 다른 파일시스템의 루트 디렉터리로 접근하게 된다.

2. 디스크 관리 방법

1) 파일 데이터 할당 방법

외부 단편화 문제가 발생하는지, 직접 접근이 발생하는지를 중점적으로 판단한다.

(1) 연속 할당(contiguous allocation)

  • 데이터를 연속적으로 할당하는 방식이다. 고정된 크기로 할당하기 때문에 외부 단편화 문제가 발생한다. 파일 크기가 증가할 수 있기에 파일 크기를 정하는 게 어렵다.
  • 연속적으로 할당하기 때문에 헤드가 한 번 섹터에 접근하면 많은 데이터를 읽을 수 있다. 파일의 위치를 인덱스로 알고 있기 때문에 직접 접근이 가능하다는 장점도 있다.

(2) 불연속 할당(linked allocation)

  • 현재 섹터에 데이터를 담고, 다음 데이터가 있는 섹터의 위치를 저장하는 포인터를 선언하는 방식이다. 외부 단편화 문제가 발생하지 않는다.
  • 직접 접근이 불가능하다. 한 섹터가 유실되면 많은 데이터를 잃을 수 있다. 추가로 포인터를 선언하기에 공간 오버헤드가 발생한다.

FAT 파일 시스템에서는 포인터의 위치 정보 테이블을 만들어 별도로 저장하여 직접 접근이 가능하고, 공간 오버헤드도 해소하였다.

(3) 인덱스 할당(indexed allocation)

  • 파일의 모든 데이터 블록 포인터가 한 개 또는 여러 개의 인덱스 블록에 저장한다. 외부 단편화 문제가 발생하지 않고, 직접 접근이 가능하다.
  • 작은 파일의 경우 공간이 낭비된다. 추가로 위치 정보를 담는 블록을 선언하기 때문이다. 매우 큰 파일의 경우 하나의 block으로 저장하기 어렵다.

Unix, ext 파일 시스템이 단점을 보완하여 채용한 방법이다.

2) 빈 공간 관리 방법

(1) 비트맵

  • 디스크의 각 블록이 사용 중인지 아닌지 비트 하나로 나타낸다. 사용할 수 있는 공간은 0, 사용 중인 공간은 1로 나타낸다.
  • 연속적인 n개의 빈 블록을 찾는 데 효과적이다. n개의 빈 공간이 있는지 비트연산으로 빠르게 검색할 수 있다(sliding window).

(2) Linked list

  • 사용 가능한 블록(free block)을 연결 리스트로 연결한다. 빈 공간을 관리하기 위한 추가적인 데이터 구조가 필요하다.
  • 아이노드에 첫 번째 프리 블록의 위치를 저장하고, 다음 프리 블록의 위치는 각 프리 블럭에 기록하여 관리할 수 있다.
  • 연속적인 가용 공간을 찾기가 어렵다.

(3) Grouping

  • linked list 방법의 변형으로 연속된 빈 공간을 그룹으로 관리한다.
  • 첫 번째 free block이 n개의 pointer를 가진다. n-1 포인터는 비어있는 데이터 블록을 가리킨다. 마지막 포인터가 가리키는 block은 또다시 n pointer를 가진다.
  • 공간 분할과 병합을 관리해야 한다는 추가적인 복잡성이 있다.

(4) Counting

  • 연속된 빈 공간 블록의 시작 주소와 이어지는 빈 블록의 수를 저장하여 빈 공간을 관리한다.
  • 연속적인 공간을 쉽게 하며 공간을 효율적으로 사용할 수 있다.
  • 프로그램들이 종종 여러 개의 연속적인 block을 반납한다는 성질에 착안한 방식이다.

3) 파일 보호 방법

  • 각 파일에 대해 누구에게 어떤 유형의 접근(read/write/execution)을 허락할 것인지 정하는 파일 보호 정책을 말한다.

(1) Access Control Matrix

1. Access Control List(ACL)

  • 파일별로 누구에게 어떤 접근 권한이 있는지 표시한다.

2. Capability

  • 사용자별로 자신이 접근 권한을 가진 파일 및 해당 권한을 표시한다.

파일, 사용자별로 기준을 나누는 것은 오버헤드가 크다. 총 9비트를 사용하면 그룹별로 권한을 부여할 수 있다.

2) Grouping

  • 전체 user를 owner, group, public 세 그룹으로 구분한다.
  • 각 파일에 대한 세 그룹의 접근 권한(읽기, 쓰기, 실행)을 3비트씩 표시한다.

3) Password

  • 파일 또는 디렉터리마다 password를 두는 방법이다.
  • 접근 권한별 password를 적용한다면 관리문제가 발생한다. 모든 접근 권한에 대해 하나의 password만 적용한다면 보안상 취약해진다.

4) 순차 접근과 직접 접근

  • 시스템이 제공하는 파일 정보의 접근 방식을 말한다. 순차 접근(sequential access)의 경우 카세트테이프를 사용하는 것처럼 순차적으로 접근해야 하는 방식을 말하며, 직접 접근의 경우(direct access) 파일을 구성하는 레코드를 임의의 순서로 접근할 수 있다.

3. RAID(Redundant Array of Inexpensive Disk)

외부적으로 RAID는 하나의 디스크처럼 보인다. 안을 들여다보면 RAID는 여러 개의 디스크와 메모리, 시스템을 관리하기 위한 하나 또는 그 이상의 프로세서로 이루어진 복잡한 기계이다.

1) 사용 목적

(1) 성능

  • RAID를 사용하면 성능상 이점이 있다. 디스크 여러 개를 병렬적으로 사용하기에 I/O시간이 크게 개선될 수 있기 때문이다.

(2) 가용성

  • 가용성이란 프로그램이 정상적으로 사용이 가능한 상태를 말한다. 높은 가용성이란 어떤 문제가 생겨도 이를 복구할 수 있는 능력이다. RAID는 여러 디스크에 분산 저장하여 가용성을 높일 수 있다.

(3) 용량

  • 데이터의 양이 많아지면 더 많은 디스크 공간이 필요하다. RAID는 여러 개의 디스크로 이루어져 있기에 많은 용량을 제공할 수 있다.

2) RAID 평가 방법

  • RAID를 구성하는 다양한 방법이 있고, 방법마다 다양한 특성을 보인다. 여러 방법 간 장단점을 이해하기 위해서 크게 3가지 평가 기준을 사용한다. 위에서 살펴본 대로 성능, 가용성, 용량을 기준으로 평가한다.

3) RAID 종류

(1) RAID 레벨 0: 스트라이핑(striping)

  • RAID 레벨 0은 엄밀히 말하면 중복 저장을 하지 않기 때문에 RAID 레벨이 아니다. 하지만 스트라이핑(Striping)이라고 알려진 이 방식은 성능과 용량에 대한 훌룡한 상한 기준을 제시한다.

  • 아래 그림을 보면 동작 방식을 이해할 수 있다. 디스크 배열의 블록들을 라운드 로빈 방식으로 디스크를 가로질러 펼치는 것이다. 해당 접근법은 병렬성을 가장 잘 활용할 수 있도록 설계되었다.

  • 성능과 용량 측면에서는 훌룡하지만 어느 디스크라도 고장 나면 전체 데이터가 손실이 난다는 문제가 있다.

청크 크기는 성능에 영향을 준다. 작은 청크 크기가 의미하는 것은 많은 파일들이 여러 디스크에 걸쳐서 스프라이프 된다는 말이며 병렬성이 증가한다. 하지만 블록의 위치를 여러 디스크에서 찾아야 하므로 위치 찾기 시간이 늘어난다. 반면 청크 크기가 크다면 파일 내 병렬성은 줄어들지만, 디스크 탐색 시간은 줄어든다. 따라서 최적 청크 크기를 결정하기 위해서는 워크로드에 대한 심도 있는 이해가 필요하다.

(2) RAID 레벨 1: 미러링(mirroring)

  • 첫 번째 RAID레벨은 미러링으로 시스템에서는 각 블록에 대해 하나 이상의 사본을 둔다.

  • 사본은 서로 다른 디스크에 저장되어 디스크 고장에 대처할 수 있게 한다.

  • 미러링된 배열에서 블록을 읽을 때 RAID는 원본을 읽을 건지 사본을 읽을건지 선택할 수 있다. 반면에 블록을 쓰는 경우에는 그렇게 선택할 수 없다. 신뢰성 확보를 위해 두 개의 디스크에 모두 써야 한다. 물론 쓰기 작업이 병렬적으로 실행될 수 있다.

  • 대략 최대 대역폭의 절반을 얻을 수 있으며 (성능 양호), 최대 사용할 수 있는 용량의 반 정도 사용할 수 있다(용량 비용 많음). 최대 N/2N/2개 결함까지 감내할 수 있다.

(3) RAID 레벨 4: 패리티(Parity)

  • 패리티 기반의 접근 방법은 저장 공간을 더 적게 사용하려고 하여 미러링 기반 시스템이 지불하는 엄청난 공간 낭비를 극복하려고 시도한다. 그 대신 성능이라는 비용을 지불한다.
  • 패리티를 계산하기 위해서는 스트라이프에 속해 있는 블록 중 하나의 블록이 고장 나더라도 견딜 수 있게 하는 수학 함수가 필요하다. XOR은 1이 짝수 개일 때는 0을 반환하고 홀수 개의 1이 있으면 1을 반환한다. 큰 RAID 시스템에서도 사용할 수 있게 감산적 패리티(subtractive parity) 방법을 사용한다. 비교하는 방법은 다음과 같다.
    • 먼저 옛날 값을 각각 읽어온다. (C2old=1,Pold=1)C2_{old}=1, P_{old}=1)
    • 옛날 값이 동일하다면(C2new=C2oldC2_{new}=C2_{old}) 패리티 값이 그대로 유지되리라는 것을 안다. (Pnew=Pold)P_{new}= P_{old})
    • 그러나 만약 서로 다르다면, 이전의 패리티 값을 현재 상태의 반대 값으로 뒤집어야 한다. 이전 값이 0이었다면 1이 되고, 1이었다면 0이 된다.
  • 다음 예시에서 병목 현상을 확인할 수 있다.
  • 4번과 13번을 갱신하는 두 개의 작은 크기의 쓰기가 RAID-4에 거의 동시에 요청되었다면? 해당 데이터는 0번과 1번에 있으며 읽기 쓰기는 병렬적으로 일어날 수 있다. 문제는 패러디 디스크에 있다. 두 요청 모두 블록 4와 13번의 패리티 블록인 1번과 3번을 읽어야 한다. 즉, 패리티 블록을 읽을 때 병목 현상이 발생한다. 이는 디스크를 시스템에 추가한다고 해서 병목 현상을 해결할 수 있는 게 아닌 구조적인 문제이다.

(4) RAID 레벨 5: 순환 패리티

  • RAID-5는 RAID-4와 거의 동일하게 동작하지만, 패리티 블록을 순환(rotate)시킨다는 점이 다르다.
  • 병목 현상을 없애기 위해 전체 디스크에 걸쳐 순환 배치된다.
  • 기본적으로 RAID-5 RAID-4와 성능이 동일하기에 시장에서 RAID-4를 완전히 대체하였다.

(5) 성능 비교

  • 결론적으로 성능만을 원하고 신뢰성을 고려하지 않는다면 스트라이핑이 가장 좋다. 하지만 임의 I/O 성능과 신뢰성을 원한다면 미러링이 최선이다. 하지만 용량에서 손해를 본다. 용량과 신뢰성이 목적이라면 RAID-5가 가장 적당하다.

참고 자료

  • 2014 이화여대 반효경 운영체제 강의
  • 운영체제, 아주 쉬운 세 가지 이야기

profile
꾸준하게

0개의 댓글