1. 파일시스템 일반
메모리 관리 기법 vs 파일 시스템
- 메모리 관리 기법 : 주 기억 장치라고 불리는 저장장소를 관리하는 소프트웨어
- 파일 시스템 : 보조 기억 장치라고 불리는 저장장치를 관리하는 소프트웨어
- 보조 기억 장치 : 하드디스크 등등
- 둘 다 공통적으로 기억장치를 관리함
- 기억 장치는 한정되어있는 자원이기 때문에 최대한 공간을 아껴서 사용해야 함 → 내부/외부 단편화를 최소화 하기 위해 노력함
- 기억 장치는 한정되어있는 자원이기 때문에 최대한 공간을 아껴서 사용해야 함 → 내부/외부 단편화를 최소화 하기 위해 노력함
- 차이점 : ‘이름’ 이라는 특성이 다름(Naming)
- 차이점을 제외하고는 같은 소프트웨어라고 볼 수도 있음
- ‘이름’특성은 파일 시스템에만 존재
파일시스템
- 사용자에게 이름이라는 속성으로 접근되는 추상적인 객체인 ‘파일’이라는 개념을 제공함으로써 영속적인 객체의 저장을 지원하는 소프트웨어
- ‘이름’을 입력으로 받아 해당 데이터를 리턴해주는 소프트웨어
- 하드디스크에 저장하는 정보
- 메타 데이터
- 파일의 속성 정보나 데이터 블록 인덱스 정보 등이 해당 ex) ‘이름‘, 파일정보, 인덱싱 정보
- 파일의 속성 정보나 데이터 블록 인덱스 정보 등이 해당 ex) ‘이름‘, 파일정보, 인덱싱 정보
- 사용자 데이터
- 사용자가 실제 기록하려 했던 내용
- 메타 데이터
- 처음 데이터를 기록할 때 실제 데이터 뿐만 아니라 부가적인 정보를 기록하여 사용자가 요청시 ‘파일’로 제공함
2. 디스크 구조와 블록 관리 기법
디스크
- 구성요소
- 원판 (plotter)
- 원 모양의 트랙(track)들이 존재 ** 실린더(cylinder) : 모든 원판에서 같은 위치를 갖는 트랙들의 집합
- 트랙은 몇 개의 섹터(sector)로 구분됨 ** 섹터
- 디스크에서 데이터를 읽거나 기록할 떄 기본 단위
- 일반적으로 512byte의 크기를 가짐
- 원 모양의 트랙(track)들이 존재 ** 실린더(cylinder) : 모든 원판에서 같은 위치를 갖는 트랙들의 집합
- 팔 (arm)
- 헤드 (head)
- 각 원판의 읽기/쓰기가 가능한 면마다 하나씩 존재
- 원판 (plotter)
- 헤드 개수, 트랙(또는 실린더) 개수 그리고 각 트랙별 섹터 개수 등이 정해지면 해당 디스크의 물리적 특성을 결정할 수 있음
디스크에서 데이터를 접근하는데 걸리는 시간
- 탐색 시간 (seek time)
- 헤드를 요청한 데이터가 존재하는 트랙 위치까지 이동하는데 걸리는 시간
- 회전 지연 시간 (rotational latency)
- 요청한 섹터가 헤드 아래로 위치될 때까지 디스크 원판을 회전시키는데 걸리는 시간
- 데이터 전송 시간 (transmission time)
- 헤드가 섹터의 내용을 읽거나 기록하는데 걸리는 시간
디스크의 논리적인 구조
💡 파일 시스템은 디스크를 물리적인 구조로 보지 않고 논리적인 디스크 블록 (disk block)들의 집합으로 본다.
- 디스크 블록의 크기는 일반적으로 페이지 프레임의 크기와 같음
- 파일시스템 성능의 최대 병목 요소 : 디스크 I/O ⇒ 최근 디스크 블록의 크기를 더 크게 설정하는 경향이 있음 → 디스크 블록의 크기가 클수록 한 번의 Disk IO로 더 많은 데이터를 메모리로 읽어 들일 수 있기 때문 But!! 디스크 블록의 크기와 ‘속도’ 측면에서의 성능은 비례하지만, ‘공간효율성’면에서의 성능은 반비례함 (평균적으로 마지막 블록의 절반은 낭비됨)
- 만약 디스크 블록의 크기가 4KB이고 섹터의 크기가 512byte라면 하나의 디스크 블록에 8개의 섹터가 대응됨 → 디스크 디바이스 드라이버 또는 디스크 컨트롤러가 디스크 블록의 번호를 이에 대응되는 섹터들로 매핑시킴
디스크 블록 할당 방법
- 연속(sequential) 할당
- 파일에게 연속된 디스크 블록을 할당하는 방법
- 불연속 할당에 비해 읽는 속도가 빠름 → 디스크 탐색 시간을 줄일 수 있기 때문
- 파일의 크기가 변하면 문제가 될 수 있음
- 파일의 크기가 더 커진다면 연속 할당을 위해 기존에 있던 디스크 블록들을 더 넓은 곳으로 복사한 후 새로운 내용을 추가해야 함 → 성능 문제 발생
- 파일의 크기가 더 커진다면 연속 할당을 위해 기존에 있던 디스크 블록들을 더 넓은 곳으로 복사한 후 새로운 내용을 추가해야 함 → 성능 문제 발생
- 불연속(non-sequential) 할당
- 같은 파일에 속한 디스크 블록들을 연속적으로 저장하지 않음
- 파일에 속한 디스크 블록들이 어디에 위치하고 있는지에 대한 정보를 기록해 두어야 함 → 블록체인 기법, 인덱스 블록 기법, FAT(File Allocation Table)기법 등을 사용
블록체인 기법
- 같은 파일에 속한 디스크 블록들을 체인으로 연결해 놓는 방법
- 특정 파일에 속한 첫 번째 디스크 블록에 가면 포인터를 이용해 다음 블록의 위치를 찾아 갈 수 있음
- 단점
- 파일의 끝 부분을 읽으려는 경우 어쩔 수 없이 앞부분의 데이터 블록을 읽어야 함
- 중간 한 블록이 유실된 경우 나머지 데이터까지 모두 잃게 됨
인덱스 블록 기법
- 위치 정보들을 기록한 인덱스 블록을 따로 사용하는 방법
- 단점
- 인덱스 블록이 유실되면 파일의 데이터 전체가 소실됨
- 인덱스 블록을 위한 별도의 공간이 필요함
- 파일이 커져 인덱스 블록이 가득 찰 경우 이를 해결할 방법이 필요
FAT 기법
- 같은 파일에 속해 있는 블록들의 위치를 FAT이라는 자료구조에 기록해 놓는 방법
- 파일시스템이 관리하는 공간 내에 전역적으로 존재하는 FAT 구조를 사용하여 파일에 속해있는 데이터를 찾아가는 방법 → 파일시스템 전체적으로 하나의 FAT 존재
- FF : 파일의 끝을 의미, 0 : free상태 의미
- 파일의 중간 데이터를 읽기 위해 처음 블록부터 읽을 필요 없음
- FAT 구조 유실 → 파일 시스템 내의 모든 파일 소실을 의미 ⇒ FAT 내용을 중복하여 관리함
** 리눅스에서 주로 사용되는 파일시스템인 Ext2나 Ext3는 인덱스 블록 기법과 유사한 기법을 사용 → inode
3. FAT 파일시스템
디렉터리 엔트리
- msdos 파일시스템에서는 각 파일마다 디렉터리 엔트리를 하나씩 가짐
- 디렉터리 엔트리가 모여 디렉터리를 구성
- 리눅스에서는 디렉터리와 파일은 별도 구분없이 파일로 취급됨 → 디렉터리 : 자신이 포함하는 파일의 이름 들을 데이터로 가지고 있는 특수한 파일
- ‘/’의 디렉터리 엔트리를 읽어 데이터 블록을 찾아가며 파일을 찾음
- 파일 시스템은 ‘/’의 디렉터리 엔트리 번호를 위해 ‘최상위 디렉터리가 위치하고 있는 곳’ 같은 정보를 적어서 자신이 관리하는 공간의 맨 앞부분에 적어둠 → 디스크 맨 앞부분만 읽으면 ’/‘ 디렉터리의 위치를 알 수 있음 → ’/‘ 디렉터리 하위에 존재하는 다른 모든 파일을 찾을 수 있음 ⇒ 슈퍼 블록 (super block)
- 파일 시스템은 ‘/’의 디렉터리 엔트리 번호를 위해 ‘최상위 디렉터리가 위치하고 있는 곳’ 같은 정보를 적어서 자신이 관리하는 공간의 맨 앞부분에 적어둠 → 디스크 맨 앞부분만 읽으면 ’/‘ 디렉터리의 위치를 알 수 있음 → ’/‘ 디렉터리 하위에 존재하는 다른 모든 파일을 찾을 수 있음 ⇒ 슈퍼 블록 (super block)
4. inode 구조
- ext 계열 파일시스템이 채택하고 있는 구조
i_mode
- 16비트로 구성
- 상위 4개 비트 : 파일 유형
- 정규파일, 디렉터리, 문자 장치 파일, 블록 장치 파일, 링크 파일, 파이프, 소켓 등
- 그 다음으로 위치한 3개 비트 : 특별한 목적으로 사용
- u비트 (setuid 비트)
- 파일이 수행될 때 수행시킨 태스크의 소유자 권한이 아닌, 파일을 생성한 사용자의 권한으로 동작할 수 있게 함
- g비트 (set-gid(set group id) 비트)
- u비트와 유사하게 사용됨
- s비트 (sticky 비트)
- 태스크가 메모리에서 쫓겨날 때, swap공간에 유지되도록 할 때, 또는 디렉터리에 대한 접근 제어에 사용됨
- u비트 (setuid 비트)
- 그 다음 9개 비트 : 파일의 접근 제어(읽기/쓰기/수행)에 사용
- 처음 3개 : 사용자에 대한 접근 제어
- 그다음 3개 : 그룹에 대한 접근 제어
- 그 다음 3개 : 다른 사용자들에 대한 접근 제어
i_block[15] 필드
- 파일에 속한 디스크 블록들의 위치를 관리하기 위해 사용
- 12개 : 직접 블록(direct block), 3개 : 간접 블록(indirect block)
- 직접 블록
- 실제 파일의 내용을 담고 있는 디스크의 데이터 블록을 가리키는 포인터
- 간접 블록
- 인덱스 블록(디스크 블록을 가리키는 포인터들을 갖는 블록)을 가리키는 포인터
- 구분
- 단일 간접 블록 (single indirect block) : 하나의 인덱스 블록을 가짐
- 이중 간접 블록 (double indirect block) : 2단계의 인덱스 블록을 가짐
- 삼중 간접 블록 (triple indirect block) : 3단계의 인덱스 블록을 가짐
inode 구조에서 지원하는 파일 크기
- (가정) 디스크 블록의 크기 : 4KB, 인덱스의 각 포인터(엔트리) 크기 : 4byte
직접 블록으로 지원할 수 있는 파일 크기 : 48KB
단일 간접 블록으로 지원할 수 있는 파일 크기 : 4MB
이중 간접 블록으로 지원할 수 있는 파일 크기 : 4GB
삼중 간접 블록으로 지원할 수 있는 파일 크기 : 4TB
⇒ 최대 총 48KB+4MB+4GB+4TB 지원
But! 실제 리눅스가 지원하는 파일의 크기는 4GB or 2GB 밖에 안됨
→ 커널 내부의 파일과 관련된 함수들이 사용하는 변수나 인자들이 아직 32비트로 구현되었기 때문
디렉터리 엔트리
- 파일의 이름과 inode를 연결할 때 사용
5. Ext2 파일 시스템
- Ext2 파일시스템을 기반으로, 다양한 형태의 결함(fault)에 강한 저널링 기능을 추가한 것이 Ext3와 Ext4 파일 시스템임 ⇒ Ext3와 Ext4는 디스크에 실제 저장하는 대부분의 내용이 Ext2와 호환됨
- 파일 시스템은 디스크를 관리함
- IDE 방식 디스크가 장착된다면 /dev 디렉터리에 “hd”라는 이름의 블록 장치 파일로 접근됨
- 만일 디스크가 2개 이상 존재하면 첫 번째 디스크는 hda, 두 번째 디스크는 hdb라는 이름이 붙음
- SCSI 방식 디스크라면 “sd”라는 이름의 블록 장치 파일로 접근됨
- IDE 방식 디스크가 장착된다면 /dev 디렉터리에 “hd”라는 이름의 블록 장치 파일로 접근됨
- 디스크가 시스템에 장착되면 디스크는 사용자가 원하는 개수만큼의 논리적인 영역(partition)으로 분할될 수 있음
- 각 파티션에도 장치 파일 이름이 붙으며 첫 번째 디스크의 파티션은 hda1, hda2, hda3 … 등으로, 두 번째 디스크의 파티션은 hdb1, hdb2, hdb3 … 등으로 이름이 붙음
- 파일 시스템은 각 파티션 당 하나씩 만들어 짐
- 파일 시스템을 만들면 해당 파티션은 복수개의 블록 그룹으로 나뉨 → 같은 파일에 대한 inode와 디스크 블록들을 인접한 실린더에 유지하여 디스크 탐색시간을 줄이려 하는데 Ext2는 인접한 실린더를 블록 그룹으로 정의함
- 파일 시스템을 만들면 해당 파티션은 복수개의 블록 그룹으로 나뉨 → 같은 파일에 대한 inode와 디스크 블록들을 인접한 실린더에 유지하여 디스크 탐색시간을 줄이려 하는데 Ext2는 인접한 실린더를 블록 그룹으로 정의함
- 블록 그룹 (Block Group) 구분
- 수퍼블록
- 블록 그룹 디스크립터
- 디스크 블록을 위한 bitmap
- inode를 위한 bitmap
- inode table 영역 : 파일시스템 구축시에 계산된 값으로 결정되며 고정된 값을 가짐
- data blocks 영역
- 수퍼블록과 그룹 디스크립터는 소실되면 안 되는 중요한 정보이기 떄문에 각 블록 그룹마다 중복하여 기록함 → 일부 소실 되어도 다른 그룹에 존재하는 내용을 기반으로 복구 가능
- Ext2파일 시스템 구축 후 마운트하면 자동으로 lost+found 파일이 생성됨
- 정상적으로 연결되어 있지 않은 디렉터리 엔트리를 넣어 두는 창고 역할을 함
- 사용자에 의해 삭제되어도 자동으로 새로 생성한 후 내용을 기록함
6. Ext3 파일 시스템과 Ext4 파일시스템
Ext3
- 기본 설계 철학 : 특정 파일로 인해 야기된 문제가 파일시스템 전체에 주는 영향을 없애고, 해당 파일로 국한시키자
- 성능 향상을 위해 ext2에 없던 기능들이 추가되었지만 ext2와의 호환성을 위해 디스크 저장 자료구조 대부분이 ext2와 같도록 설계됨
- 신뢰성과 성능 향상을 위해 도입한 기술
해쉬(Hash)기반 HTree 기술 도입
- ext2 는 디렉터리를 단순 연결 리스트로 관리함 → 한 디렉터리 내에 많은 파일이 존재하는 경우 디렉터리 내의 파일을 검색하기 위해 걸리는 시간이 선형적으로 증가
- 이를 해결하기 위해 HTree 도입 → 많은 파일이 있어도 상수 시간 안에 접근 가능
- ext2 는 디렉터리를 단순 연결 리스트로 관리함 → 한 디렉터리 내에 많은 파일이 존재하는 경우 디렉터리 내의 파일을 검색하기 위해 걸리는 시간이 선형적으로 증가
강력한 저널링(journaling) 기능 지원
- 전원이 나가는 경우와 같은 결함을 허용(fault tolerant) 할 수 있도록 저널링 기법 도입
- 별도의 저널링 공간(저널)을 통해 제공 → 디스크에서 별도의 공간을 차지함
- 저널링 모드
- Journal
- 파일시스템에 데이터를 기록하기 전에 모든 데이터를 저널에 기록하고, 나중에 실제 파일 시스템에 복사하는 방식
- 속도 측면에서의 성능은 떨어지지만 가장 높은 안정성 제공
- Ordered
- 일반적으로 사용되는 모드
- 메타데이터에 한해 저널을 사용, 사용자 데이터는 실제 파일 시스템에 저장함
- 저널에 기록된 메타데이터는 쓰기 순서를 보장
- Writeback
- Ordered와 유사하나 쓰기 순서는 보장되지 않음
- 속도 면에서 성능이 좋아질 수 있지만 안정성은 떨어짐
- Journal
- 파일시스템을 마운트할 때 -j 옵션을 줌으로써 가능
- 커널 내의 JBD(Journaling Block Device)와 유기적으로 동작함
시스템의 가용시간을 향상시킬 수 있는 online-resizing
- LVM(Logical Volume Manager)를 통해 파일시스템이 관리하고 있는 용량을 동적으로 늘릴 수 이쓴 기법 도입Ext4 파일시스템 특징
선할당(preallovation) 기법과 지연 할당(Delayed allocation) 기법 도입
- 선할당
- 파일시스템의 일관성을 높이며, 동시에 속도를 향상시키기 위해 도입
- 파일 생성시 미리 일정 개수의 블록을 할당함으로써 물리적으로 연속적인 블록을 할당하여 성능을 향상시키려는 기법
- 지연 할당
- 단편화 방지를 위해 도입
- 자유 블록 카운트만 갱신하고 실제 할당은 뒤로 미루는 방식
- 선할당
extent기반 데이터 블록 유지
- 대용량 파일의 메타데이터를 줄일 수 있음
- 블록의 번호를 전부 저장하지 않고, 시작 블록의 번호와 블록 개수 정보만을 저장하여 유지
- 데이터 블록을 인덱싱하기 위한 시간이 줄어듬
이외에 ext3 저널링 기능의 성능 향상을 위한 저널링 체크섬, 대용량 파일 시스템과 큰 크기 파일의 지원, 온라인 단편화 제거 지원 등의 기능 추가
7. 가상 파일시스템 (Virtual File System)
- 파일시스템과 사용자 태스크 사이에 있는 가상적인 층
- 상위 계층에서 단일한 함수를 통해 파일시스템에 접근하면, 인자에 담겨있는 파일이름을 보고 파일을 관리하고 있는 파일 시스템이 무엇인지 판단 → 해당 파일시스템의 함수를 살펴보고 사용자가 원하는 일을 해줄 수 있는 파일시스템 고유 함수 호출 → 파일시스템 함수가 결과를 리턴하면 사용자에게 이를 건내줌
- 리눅스에서 다양한 파일시스템을 지원하는 것이 가능해짐 ++ 커널 내부 상태를 볼 수 있는 proc 파일시스템, 장치를 통합 관리하는 sysfs 등의 특별한 파일시스템도 지원
- 사용자 태스크는 자신이 접근 하는 파일이 어느 파이시스템에 저장되어 있는지 굳이 알 필요없이 일관된 POSIX 표준 인터페이스만을 사용하는 것이 가능해짐 → 사용자 태스크는 VFS라는 가상적인 파일시스템만을 알고있으면 충분해짐
- VFS 내에 4개의 객체를 도입하여 관리
- 수퍼 블록 객체
- 현재 사용중인(마운트 된) 파일시스템 당 하나씩 주어짐
- 아이노드 객체
- 특정 ‘파일’과 관련된 정보를 담기 위한 구조체
- VFS가 아이노드 객체를 생성하고 파일시스템에 특정 파일에 대한 정보를 요청하면 파일시스템은 자신이 관리하고 있는 영역에서 파일의 메타데이터를 읽어 아이노드 객체에 채워줌
- 파일 객체
- 태스크가 open한 파일과 연관되어 있는 정보를 관리함
- 각 태스크가 아이노드 객체에 접근하는 동안만 메모리상에 유지됨
- 디엔트리 객체
- 아이노드 객체를 연관된 파일 객체에 연결시킬 때 이 관계를 좀 더 빠르게 연결하기 위한 일종의 캐시 역할을 함
- 수퍼 블록 객체
이 글은 아래의 책을 공부 및 정리한 내용입니다.
리눅스 커널 내부구조 - 예스24