ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파일 시스템 - 레이아웃
    운영체제 2024. 5. 20. 19:29

    다양한 종류의 파일 시스템

    UFS, FFS, EXT2/3/4,

    JFS(저널링), LFS(로그 스트럭처드), NTFS(윈도우), F2FS(플래시-프렌들리.. 삼성 꺼),

    FUSE(유저 레벨 커널 시스템), RAMFS(램), NFS(네트워크), AFS(앤드류),

    ZFS(제타바이트, 솔라리스에서 만듦), GFS(구글), FATFS, BtrFs..


    VSFS(베리 심플 파일 시스템)

    유닉스 파일 시스템 심플 버전을 구상해보자

     

    디스크

    파일 시스템 입장에서는 디스크는 그냥 4k 블록 집합으로 봄

     

    파티션

    디스크 블록들의 집합

    디스크 블록의 크기는 보통 메모리의 페이지 크기와 같음 = 4 KB

     

    VSFS에서는 디스크 블록이 64개 있다고 가정

     

    슈퍼 블록 - 0번에 할당

    파일 시스템을 관리하기 위한 메타 데이터

     

    비트맵 - 1~2번 블록

    빈 공간을 다루기 위한 메타데이터

    하나는 inode, 하나는 데이터 블록

     

    inode - 3~7번 블록

    inode는 256B -> 블록 당 16개의 inode -> 5 블록이니까 총 80개의 파일을 다룰 수 있음

     

    유저 데이터 - 8 ~ 63번 블록

     

     

    inode (index node)

    파일 정보 (mode, uid, size, time, link count, blocks ..)

    유저 데이터를 가리키는 정보가 있음

    - 다이렉트 블록 포인터 12개 (12 * 4KB)

    - 싱글 인다이렉트 블록 포인터 1개 (1024 * 4 KB = 4MB, 왜 1024냐면 블록 하나가 4096 B인데 포인터는 4바이트 단위)

    - 더블 인다이렉트 블록 포인터 1개 (1024 * 1024 * 4 KB = 4GB)

    - 트리플 인다이렉트 블록 포인터 1개 (1024 * 1024 * 1024 * 4 KB = 4TB)

    ==> 최대 파일 사이즈 4.444 TB

     

    inode 블록 포인터 : 인밸런스드 트리

    왜 이러한 형태를 채용했나?

    대부분 컴퓨터에 있는 파일 크기는 작음

    작은 파일들은 다이렉트 포인터로 빠르게 접근하는게 좋음

    -> 작은 파일들과 큰 파일들을 효율적으로 서비스 가능함


    파일 시스템 블록 할당 예시 :

    hello.c 7KB

    a.out 70KB

    0번 -> 슈퍼블록

    1번 -> inode 비트맵

    2번 -> inode 비트맵

     

    3~7번 -> inode 테이블

    - inode 테이블

    - 0번 -> 루트의 inode(8번 데이터 블록 가리킴)

    - 1번 ->  hello.c의 inode(9, 10번 데이터 블록을 가리킴)

    - 2번 -> a.out의 inode (11~23 데이터블록 가리키는데, 23번은 indrect pointer)

     

    8번 -> 루트의 데이터블록, 파일 이름과 inode 번호의 쌍

    - . : 0

    - .. : 0

    - hello.c : 1

    - a,out : 2

     

    9번 -> hello.c 데이터블록

    10번 -> hello.c 데이터블록

     

    11~22번 -> a.out의 데이터블록

    23번 -> a.out 싱글 인다이렉트 포인터가 가리키는 인덱스 블록: 24~29번 가리킴

    24 ~ 29번 -> 인덱스 블록에서 가리키는 데이터블록

     

    어떻게 /a.out을 읽느냐?

    루트의 inode부터 찾아감 -> 8번 데이터블록을 찾아감

    -> 2번 inode를 가리키는 걸 확인하고 찾아감 -> 11번부터 데이터를 읽을 것


    inode, data는 어떻게 위치를 찾는가

     

    inode

    디렉토리에 정보가 있음: <file name, i_number>

    i_number가 inode 찾아갈 인덱스

     

    예) i_number가 33

    33 / 16 = 2 .. 1

    2번 째 inode 블럭의 1번으로 찾아가면 33번임 (0번부터 시작)

    ** 왜 16으로 나누냐: inode 크기는 256바이트, 4096 / 256 = 16 -> 블럭 하나에 16개의 inode 가능

     

    예) i_number가 56

    56 / 16 = 3 .. 8

    3번 i블록의 8번으로

     

    유저 데이터

    1. inode 찾기

    2. 파일의 current_offset / disk block size = q .. r

    inode에서 q번째 데이터 블록 포인터가 가리키는 위치로

    3. 블록의 r번째 데이터부터 읽으면 됨


    디렉토리

    서로 연관된 파일의 집합

     

    유저 관점: 같은 위치에 있는 서로 연관된 파일의 모음

    시스템 관점: 그 디렉토리에 있는 파일 이름과 inode 번호의 집합

     

    프리 스페이스 관리

    - 비트맵: 블록(or inode) 당 1비트 -> 사용 중인지 사용 가능한지를 나타냄

    - 대체적인 방법 : 프리 리스트, 트리 ..

    - 선할당: 3KB 요청이 오면 블록 하나만 필요한데 연속적으로 여러 개 할당

         => 나중에 파일 크기가 커졌을 때 연속된 공간 사용 가능 -> 시크 타임 감소 가능


    디스크로부터 파일을 읽는 경우

    /foo/bar를 읽는다고 가정(12KB)

     

    open(bar)

    read:root-inode => read:root-data => read:foo-inode => read:foo-data => read:bar-inode

    총 5번의 I/O

     

    read()

    read:bar-inode => read:bar-data[0] => write:bar-inode

    총 3번의 I/O

    wrtie는 마지막 접근 시간 같은 거 기록해야 해서 필요

     

    read()

    read()

     

    close()는 메모리 상에서만 작동하고 디스크 상에선 액션이 없음


    디스크에 파일을 쓰는 경우

    create /foo/bar, 12KB

     

    create(/foo/bar)

    read:root-inode => read:root-data => read:foo-inode => read:inode-bmap => write:inode-bmap

    read:foo-data => read:bar-inode => write:bar-inode => write:foo-inode

     

    read:bar-inode는 디폴트 inode 읽는 거

    없기도 함

     

    write()

    read:bar-inode => read:data-bmap => write:data=bmap => write:bar-data[0] => write:bar-inode

    한 번 write 하려면 5번의 I/O 필요

     

    단순히 한 번 읽고 쓰기에 여러 번의 I/O가 필요함


    캐싱과 버퍼링

     

    디스크는 밀리 단위, 메모리는 마이크로 혹은 나노 단위

    cpu는 한자릿수 나노 단위

    디스크가 너무 느린데, 간단한 read, write 하나 하나에 디스크 접근을 할 순 없음

     

    솔루션

    1. 캐싱

    디스크에 있는 데이터를 메모리에 캐싱해놓기

    - 루트 inode나 루트 데이터는 자주 접근되고, 비트맵도 자주 접근되니 디램에 올려놓고 쓰는게 빠름

    - 최근에 사용된 파일의 inode와 데이터를 메모리에 캐싱

    - 캐시 공간이 꽉 차면 교체가 필요: LRU 교체 정책(Least Recently Used)

     

    메모리의 일부를 캐시 공간, 혹은 버퍼 공간으로 사용해서 자주 접근하는 데이터들을

    디스크에서 읽지 않고 메인 메모리에서 읽어서 속도를 높이는 것이 캐싱

     

    2. write buffering (지연 쓰기)

    라이트 요청 시 메모리에만 써놓고 더티하다고 표시

    -> 내용이 수정되었다고 표시하고 나중에 처리 .. I/O 줄일 수 있음

     

    원래 위의 write 예시에선 10 + 15번의 I/O가 있음

    지연쓰기를 하면 매번 write하지 않아도 됨

    데이터 비트맵을 마지막에 몰아서 쓰거나, inode 쓰기도 몰아서 쓸 수 있음

     

    혹은, 여러 I/O를 묶어서 처리 가능함

    bar[0]~[2]는 높은 확률로 연속된 섹터에 배치됨

    파일 시스템은 가능하면 연속적으로 할당하려고 하기 때문

    -> 시크 타임 가능

     

    데이터 로스나 크래시가 걱정되면 fsync() 나 direct I/O 해야 됨

    '운영체제' 카테고리의 다른 글

    Consistency / FSCK (File System Checker)  (0) 2024.06.15
    Fast File System  (0) 2024.05.28
    파일 - 인터페이스  (0) 2024.05.20
    파일과 디렉토리  (0) 2024.05.19
    Hard Disk Drive / Time io, Time rate / Disk Scheduling  (0) 2024.05.19
Designed by Tistory.