Chapter 5 파일시스템과 가상 파일시스템

리눅스가 제공하는 대표적인 객체 태스크와 파일, 파일은 디스크를 추상화 시켜서 프로그램에게 장소를 부여한다.

 

파일시스템 일반

보조 기억 장치라고 불리는 저장장치를 관리하는 소프트웨어. 주 기억 장치로는 주로 RAM과 같은 장치가 사용, 보조 기억 장치로는 주로 하드 디스크와 같은 장치가 사용된다.

 

메모리 관리 기법과 파일 시스템은 모두 기억 장치를 관리. 기억 장치는 RAM이건 하드디스크이건 한정되어 있는 자원이기 때문에 최대한 공간을 아껴서 사용해야 한다. 메모리 관리기법과 파일시스템 모두 내부/외부 단편화를 최소화 하기 위해 노력한다. 

 

메모리 관리 기법과 파일시스템 간의 차이 : 이름(Naming), 이름이라는 특성을 제외하면 메모리관리기법과 파일시스템은 비슷한 소프트웨어. 

 

파일시스템은 사용자에게 이름이라는 속성으로 접근되는 추상적인 객체인 '파일'이라는 개념을 제공, 영속적인 객체의 저장을 지원하는 소프트웨어. 이름을 입력 받아 해당 데이터를 리턴해주는 소프트웨어

하드디스크에 어떤 내용을 저장해두는가? 메다 데이터(meta data)와 사용자 데이터(user data). 메타 데이터는 파일 속성정보나 데이터 블록 인덱스 정보 등이 해당, 사용자 데이터는 사용자가 실제 기록하려 했던 내용이 해당된다.

 

파일시스템은 처음 데이터를 기록할 때 실제 데이터뿐만 아니라, 이 데이터를 추상적 자원인 '파일'로 제공해 주기 위해 부가적인 정보(파일이름,생성시간,실제데이터블록을 인덱싱하기위한정보)를 기록해야한다. 이러한 이름과 파일 정보, 인덱싱 정보를 파일시스템의 메타 데이터라고 한다.

 

디스크 구조와 블록 관리 기법

가장 일반적인 보조기억 장치 하드디스크를 예로 설명한다.

디스크는 원판(plotter), 팔(arm), 그리고 헤드(head)로 구성.

원판에는 원 모양의 트랙이 존재하며, 모든 원판에서 같은 위치를 갖는 트랙들의 집합을 실린더(sylinder)라고 한다. 트랙은 다시 몇 개의 섹터(sector)로 구분. 

섹터는 디스크에서 데이터를 읽거나 기록할 때 기본 단위가 되며, 일반적으로 섹터의 크기는 512byte

헤드는 각 원판의 읽기/쓰기가 가능한 면마다 하나씩 존재하게 된다. 따라서 헤드가 몇 개, 트랙이(또는 실린더가) 몇 개, 그리고 각 트랙마다 섹터가 몇 개 등이 결정되면 디스크의 전체 용량 등 해당 디스크의 물리적 특성을 결정 할 수 있게 된다.

 

디스크에서 데이터를 접그하는데 걸리는 시간은 탐색 시간(seek time), 회전 지연시간(rotational latency), 데이터 전송 시간(transmission time) 세 가지로 구성.

탐색 시간(seek time) : 헤드를 요청한 데이터가 존재하는 트랙 위치까지 이동하는 데 걸리는 시간

회전 지연시간(rotational latency) : 요청한 섹터가 헤드 아래로 위치될 때까지 디스크 원판을 회전시키는 데 걸리는 시간

데이터 전송 시간(transmission time) : 헤드가 섹터의 내용을 읽거나 또는 기록하는데 걸리는 시간

 

 

파일 시스템은 디스크를 논리적인 디스크 블록(disk block)들의 집합으로 본다.

디스크 블록은 0,1,2 등의 논리적인 번호를 하나씩 갖는다. 디스크 블록의 크기는 일반적으로 페이지 프레임의 크기와 같다.(4KB)

 

파일시스템 성능의 최대 병목 요소는 디스크 I/O, 최근 개발된 파일 시스템의 경우 디스크 블록의 크기를 더 크게 설정하는 경향이 있다. 디스크 블록의 크기가 클수록 한 번의 Disk IO로 더 많은 데이터를 메모리로 읽어 들일 수 있기 때문. 그러나 디스크 블록 크기와 속도 성능은 비례하지만, 공간효율성은 반비례. 평균적으로 데이터 블록 중 마지막 블록의 절반은 낭비. 디스크 블록 크기는 용도에 맞게 사용자가 결정하는 것이 바람직하다.

 

디스크블록의 번호를 이에 대응되는 섹터들로 매핑 시키는 일은 디스크 디바이스 드라이버 또는 디스크 컨트롤러가 담당한다.

 

디스크 블록의 할당과 회수 방법

연속(sequential) 할당, 파일에게 연속된 디스크 블록을 할당하는 방속도가 빠르다. 디스크 탐색시간을 줄일 수 있기 때문. 하지만 파일의 크기가 변하면 문제가 됨. 연속 할당을 위해서는 기존에 있던 디스크 블록들을 더 넓은 곳으로 복사한 후 새로운 내용을 추가해야 한다. 

불연속(non-sequential) 할당, 같은 파일에 속한 디스크 블록들을 연속적으로 저장하지 않는다. 파일의 크기가 커져도 할당 가능, 불연속 할당방법에서는 파일에 속한 디스크 블록들이 어디에 위치하고 있는지에 대한 정보를 기록해야 한다.

기록 하는 방법은 블록체인 기법, 인덱스 블록 기법, FAT(File Allocation Table)기법 등이 있다.

 

블록체인 기법 : 같은 파일에 속한 디스크 블록들을 체인으로(각 블록에 포인터를 두어 다음 블록으 위치를 기록) 연결해 놓는 방법, 특정 파일에 속한 첫 번째 디스크 블록에 가면 포인터를 이용해 다음 블록의 위치를 찾아 갈 수 있다. 그러나 lseek()같은 시스템 콜을 사용하여 파일의 끝 부분을 읽으려면 어쩔 수 없이 앞부분의 데이터 블록을 읽어야 하며, 중간 블록 유실시 나머지 데이터를 모두 잃는 단점이 존재

 

인덱스 블록 기법 : 블록들에 대한 위치 정보들을 기록한 인덱스 블록을 따로 사용하는 방법. lseek()를 사용하여 파일의 끝 부분을 접근하는 경우 데이터 블록을 일일이 읽어야 한다는 단점은 없지만, 만약 인덱스 블록이 유실되면 파일의 데이터가 전체 소실되는 문제가 있다. 또한 인덱스블록을 위한 별도의 공간이 필요하며, 파일이 커져 인덱스 블록이 가득 찰 경우 이를 해결하는 방법이 필요

 

FAT 기법 : 같은 파일에 속해 있는 블록들의 위치를 FAT라는 자료구조에 기록해 놓는 방법. 이 기법은 파일시스템이 관리하는 공간 내에 전역적으로 존재하는 FAT 구조를 사용하여 파일에 속해있는 데이터를 찾아가는 방법. 인데스 블록 기법은 파일마다 인덱스 블록이 필요한데, FAT 기법에서는 파일 시스템 전체적으로 하나의 FAT이 존재

 

FAT구조에서 FF는 파일의 끝을 의미, 0은 free상태를 나타냄. 파일의 중간 데이터를 읽기 위해 처음 블록부터 읽을 필요가 없다. FAT구조의 유실은 파일시스템 내의 모든 파일이 소실된다는 문제가 있다.

 

요즘 대부분의 FAT 구조파일시스템은 FAT 내용을 중복하여 관리. 지금까지의 각 기법들은 서로 장단점을 가지며, 리눅스에서는 주로 Ext2나 Ext3는 인덱스 블록 기법과 유사한 기법을 사용한다. 그것이 바로 inode이다.

 

 

FAT 파일시스템

파일시스템이 관리하는 데이터는 크게 메타 데이터와 유저 데이터로 구분할 수 있다. FAT파일 시스템에서 메타데이터는 FAT 테이블, 디렉토리 엔트리, 그리고 수퍼블록으로 구성된다. 

 

msdos 파일 시스템의 디렉터리 엔트리 (~/include/linux/msdos_fs.h)

msdos 파일시스템에서는 각 파일마다 디렉터리 엔트리를 하나씩갖는다. 태스크가 하나 생성되면 task_struct라는 구조체를 통해 각 태스크를 관리하던 것과 유사. 디렉터리 엔트리가 모여 디렉터리를 구성.

리눅스에서 디렉터리와 파일은 별도 구분 없이 파일로 취급. 디렉터리는 자신이 포함하는 파일의 이름 들을 데이터로 가지고 있는 특수한 파일에 불과

 

msdos 파일시스템의 디렉터리 엔트리

파일의 이름과 확장자를 담는 name 필드(11자 이름 제약의 이유). 파일의 속성과 시간정보, 파일의 크기를 담는 size, 그리고 FAT 구조를 이용해 데이터 블록을 인덱싱 하기 위해 데이터 블록 중 첫 블록의 번호를 담아놓는 start 변수 등이 존재

 

파일시스템의 디렉터리 엔트리. 각각의 파일 시스템들은 저마다 자신만의 디렉터리 구조체를 선언해 놓은 뒤, 파일이 생성될 때, 이 구조체의 각 내용들을 채워서 디스크에 저장. 사용자가 특정 이름을 가지는 파일의 내용을 읽어오도록 파일시스템에 요청한다면 파일시스템은 사용자가 요청한 파일의 이름을 가지고 디렉터리 엔트리를 찾아낸 뒤, 디렉터리 엔트리가 가지고 있는 정보를 통해 데이터 블록을 찾아서 사용자에게 제공해 주는 것이다.

 

파일시스템은 디렉터리 엔트리를 어떻게 찾을까 ? FAT 파일시스템을 예로 든다. 

사용자는 파일의 이름을 통해 데이터에 접근 시도. 파일의 이름은 현재 디렉터리 위치를 기준으로 시작되는 상대경로와 '/'(root)에서부터 시작되는 절대경로 두 가지.

리눅스는 사용자 태스킝 현재 작업 디렉터리(Current Working Directory : CWD)를 항상 task_struct 구조체에 유지하고 있기 때문에 상대경로는 언제든 절대경로로 변환 가능.

 

/home/member/sjbaek/약속.txt 파일 읽기

우선 파일시스템은 '/'의 디렉터리 엔트리를 읽어 '/'의 데이터 블록을 찾아 낼 수 있게 된다.  '/'의 첫번째 브록을 6번으로 가정. 디렉터리의 데이터블록은 디렉터리 엔트리들로 구성, 블록에서 'home'이라는 이름을 찾는다. home의 데이터 블록 11번(가정), 11번 블록을 읽어서 member 20번, 20번 블록을 읽어서 sjbaek 43번, 43번블록을 읽어보면 사용자가 요청한 '약속.txt'파일의 데이터 블록이 48번부터 1개라는 사실을 알 수 있게 된다. 그럼 이제 48번 블록을 읽어서 사용자에게 제공해 주면 된다. 48번 블록은 여지껏 살펴본 디렉터리의 데이터 블록과 똑같이 지정되지만 이번에는 엔트리가 파일 속성을 가지므로 파일의 데이터를 담고 있을 것이다.

 

'/'의 디렉터리 엔트리 번호는 어떻게 알아내는가 ? 파일시스템은 '최상위 디렉터리가 위치하고 있는 곳' 같은 정보를 적어서 자신이 관리하는 공간의 맨 앞부분에 적어둔다. 이렇게하면 언제든 디스크의 맨 앞부분만 읽으면 '/' 디렉터리의 위치를 알수 있고, 따라서 '/'디렉터리 하위에 존재하는 다른 모든 파일을 차즐 수 있게 된다. 이를 슈퍼 블록(super block)이라 부른다.

 

msdos 파일시스템의 슈퍼블록(~/fs/fat/fat.h)

sector의 크기, sector가 여러개 모여 구성되는 cluster의 크기, FAT 테이블의 위치와 크기 등의 파일시스템 전역적인 정보와 함께, root_cluster라는 변수에 '/'의 위치를 담아 놓는다.

슈퍼 블록 구조 역시 파일시스템 각각의 구현에 따라 모두 다른 모습이지만, 주요 기능과 유지해야할 정보는 개념적으로 동일. 파일시스템의 전체적인 정보와 '/'의 위치를 기억하는 것이다.

 

inode 구조

inode 구조 : 리눅스의 디폴트 파일시스템인 ex4 등, ext 계열 파일시스템이 채택하고 있다

ext2_inode (~fs/ext2/ext2.h)

i_blocks : 이 파일이 몇 개의 데이터 블록을 가지고 있는지 나타냄

i_mode : 이 inode가 관리하는 파일의 속성 및 접근 제어 정보를 유지

i_links_count : 이 inode를 가리키고 있는 파일 수를(또는 링크 수를)의미

i_uid, i_gid : 파일을 생성한 소유자의 user ID와 group ID를 의미

i_atime, i_ctime, i_mtime : 이 파일의 접근 시간, 생성시간, 수정시간을 의미, 

 

i_mode 자세히 알아보기, 16비트로 구성되며, 상위 4개의 비트는 파일의 유형을 의미, 파일의 유형은 정규파일(S_IFRGE), 디렉터리(S_IFDIR), 문자 장치 파일(S_IFCHR), 블록 장치 파일(S_IFBLK), 링크 파일(S_IFLNK), 파이프(S_IFFIFO), 소켓(S_IFSOCK) 등이 있다.

 

그 다음으로 위치한 3개의 비트는 특별한 목적으로 사용

u비트 : setuid(set user id) 비트, 파일이 수행될 때 수행시킨 태스크의 소유자 권한이 아닌, 파일을 생성한 사용자의 권한으로 동작할 수 있또록 한다

g비트 : setgid(set group)비트

s비트 : sticky비트, 태스크가 메모리에서 쫓겨날 때, swap공간에 유지되도록 할 때, 또는 디렉터리에 대한 접근 제어에 사용

그 다음 9개 비트는 파일의 접근 제어(읽기/쓰기/수행)에 사용. 처음 3개는 사용자에 대한 접근 제어에 사용, 그 다음 3개는 그룹에 대해 그리고 마지막 3개는 다른 사용자들에 대한 접근제어에 사용

 

inode 하단부 i_block[15] 필드 : 파일에 속한 디스크 블록 들의 위치를 관리하기 위해 사용, 총 15개의 엔트리 존재

12개는 직접 블록(direct block) 3개는 간접 블록(indirect block)

직접블록: 실제 파일의 내용을 담고 있는 디스크의 데이터 블록을 가리키는 포인터

간접 블록 : 인덱스 블록(디스크 블록을 가리키는 포인터들을 갖는 블록)을 가리키는 포인터. 3개의 간접 블록은 다시 단일 간접 블록(single indirect block), 이중 간접 블록(double indirect block), 그리고 삼중 간접 블록(triple indirect blcok)으로 구분.

 

단일간접블록: 하나의 인덱스 블록을 가진다

이중간접블록: 인덱스 블록을 2단계로

삼중간접블록: 인덱스 블록을 3단계로

 

Ext 계열 파일시스템의 inode 구조에서 지원할 수 있는 파일의 최대 크기는 ? 디스크 블록의 크기가 4KB라고 가정하면, 직접 블록으로 지원할 수 있는 파일의 크기는 48KB. 단일 간접블록은 하나의 인덱스 블록을 갖는다. 인덱스 블록도 결국 디스크 블록에 존재하며 따라서 인덱스 블록의 크기는 4KB이다. 인덱스 블록은 디스크 블록을 가리키는 포인터들로 구되므로, 각 포인터를 위해 4byte가 필요하다고 볼 수 있다. 그럼 하나의 인덱스 블록에는 1024개의 포인터가 존재한다.

따라서 단일 간접 블록으로 지원할 수 있는 파일의 크기는 1024개 x 4KB, 즉 4MB.

이중 간접 블록으로 지원할 수 있는 파일의 크기는 1024x1024x4KB, 즉 4GB

삼중 간접 블록으로 지원할 수 있는 파일의 크기는 1024x1024x1024x4KB, 즉 4TB

 

결국 디스크 블록의 크기가 4KB이고 인덱스의 각 포인터(엔트리) 크기가 4byte인 시스템 환경에서 inode구조가 지원할 수 있는 파일의 최대 크기는 48KB+4MB+4GB+4TB. 하나의 파일이 사용하기에는 충분히 큰 공간이며 inode가 깔끔하게 잘 설계되어 있음을 알 수 있다. 또한 48K 미만인 경 별도의 인덱스 블록을 디스크에서 읽어올 필요가 없기 때문에 Ext 계열 파일시스템은 최적의 성능을 보인다고 볼 수 있다.

 

하지만 실제로 현재 리눅스 지원하는 파일의 크기는 4GB(또는 2GB) 박에 되지 않는다. 커널 내부의 파일과 관련된 함수들이 사용하는 변수나 인자들이 아직 32비트로 구현되었기 때문. 

Ext4는 이러한 제한을 해결하여 더 큰 크기의 파일을 지원한다.

 

파일의 이름과 inode는 어떻게 연결될까? 이때 사용되는 것이 디렉터리 엔트리이다.

이 자료구조에는 우선 이 파일의 이름을 담는 name필드가 존재. 다음으로 중요한 필드는 파일의 inode번호 Ext2 파일시스템은 모든 파일에게 고유한 inode 번호를 부여 한다(ext3도 동일). 이 번호는 파일시스템이 관리하는 공간의 앞부분에 위치하고있는 inode의 테이블에서의 번호를 의미한다.

Ext2 파일시스템의 디렉터리 엔트리는 이름과 inode번호 같은 간단한 정보만을 유지하며, 실제 데이터 블록 인덱싱과 같은 세부적인 정보는 inode번호를 가지고 찾을 수 있는 inode 자료구조에 유지되는 것이다.

 

 

 

Ext2 파일시스템

Ext2 파일시스템을 기반으로, 다양한 형태의 결함(fault)에 강인한 저널링 기능을 추가한 것이 Ext3 와 Ext4 파일 시스템이다. 따라서 디스크에 실제 저장하는 대부분의 내용이 Ext2와 호환된다.

 

 

 

 

 

Ext3 파일시스템과 Ext4 파일시스템

 

 

가상 파일시스템(Virtual File System)

 

 

 

태스크 구조와 VFS 객체

 

 

 

파일시스템 제어 흐름 분석

 

 

 

 

 

163p

3번 블록 체인, 인덱스, FAT 기법의 장단점 

7번 FAT파일시스템이 구축되어 있는 임의의 파티션에 존재하던 파일을 실수로 잘못 삭제하였다. 삭제된 파일을 복구시켜보자. Ext2나 3라면 어떠한가

 

9. FAT 파일 시스템 실제 구현 10.4절 참조

+ Recent posts