파일 시스템과 디스크 관리
파일 시스템(File System) : 디스크의 추상화
디스크 드라이브(HDD/SSD)는 본질적으로 ‘블록(Block)’ 이라고 불리는 고정된 크기(512B, 4KB 등)의 데이터를 저장할 수 있는 거대한 1차원 배열(LBA, Logical Block Addressing)에 불과합니다. 사용자가 “내 리포트.docx 파일이 1,234,567번 블록부터 1,234,570번 블록까지 저장되어 있다”라고 기억하며 사용하는 것은 불가능합니다.
파일 시스템은 이 복잡한 블록의 배열을, 우리가 일상적으로 사용하는 ‘파일(File)’ 과 ‘디렉터리(Directory)’ 라는 계층 구조로 추상화하여 제공하는 OS의 핵심 구성 요소입니다.
파일(File)
“이름을 가진, 관련된 정보의 집합”입니다. OS 관점에서는 저장 장치에 데이터를 저장하는 논리적 단위입니다.
- 속성 (Metadata) : 파일은 실제 ‘데이터’ 외에, 자신을 설명하는 ‘속성 정보(메타데이터)’를 가집니다. (파일명, 유형, 크기, 소유자, 권한(rwx), 생성/수정 시간 등)
- 파일 연산 : OS는 파일을 다루기 위한 기본 시스템 콜을 제공합니다. (
Create,Read,Write,Seek(파일 내 위치 이동),Delete등)
디렉터리(Directory)
파일을 논리적으로 그룹화하여 관리하는 ‘폴더’입니다. 대부분의 현대 OS는 디렉터리 안에 또 다른 디렉터리를 포함할 수 있는 계층적 트리 구조(Hierarchical Tree Structure) 를 사용합니다.
디렉터리 자체도 OS 관점에서는 특별한 형태의 ‘파일’로 취급됩니다. 이 ‘디렉터리 파일’의 내용물은 그 안에 속한 파일들의 (파일명, 파일 메타데이터의 위치정보) 매핑 테이블입니다.
디렉터리 구현 (Directory Implementation)
이 매핑 테이블을 어떻게 구현하느냐에 따라 파일 검색 속도가 달라집니다.
- 선형 리스트 (Linear List)
(파일명, Inode 번호)의 쌍을 리스트(배열)로 순차 저장합니다.- 구현이 매우 간단합니다.
- 디렉터리 내 파일이 많아지면, 특정 파일을 찾는 데 리스트 전체를 탐색해야 하므로 검색 속도가 매우 느려집니다. ($O(n)$)
- 해시 테이블 (Hash Table)
- 파일명을 해시 함수(Hash Function)의 입력으로 사용하여, 해시 테이블의 인덱스(Index)를 얻고 해당 위치에 메타데이터 정보(Inode 번호 등)를 저장합니다.
- 파일명만 알면 해시를 통해 즉시 위치를 찾을 수 있어, 평균 검색 속도가 매우 빠릅니다. ($O(1)$) (대부분의 현대 파일 시스템이 채택)
- 해시 충돌(Collision) 처리 등 구현이 상대적으로 복잡합니다.
파티션(Partition)
파티션(또는 볼륨) 은 하나의 물리적 저장 장치(HDD, SSD)를 여러 개의 논리적인 영역으로 나눈 것입니다.
OS는 이렇게 분리된 각 파티션을 마치 별개의 디스크 드라이브처럼 인식하며, 각 파티션마다 독립적인 파일 시스템(C드라이브는 NTFS, D드라이브는 ext4)을 설치하여 데이터를 관리할 수 있습니다. 이는 운영체제 영역, 사용자 데이터 영역, 백업 영역 등을 분리하여 관리하는 데 유용합니다.
파일 보호(File Protection)
파일에 저장된 데이터를 무단 접근, 수정, 삭제로부터 보호하기 위한 메커니즘입니다. 파일은 여러 사용자가 공유할 수 있으므로, 각 파일에 대해 ‘누구에게(Subject)’ ‘어떤 권한(Access Right)’ 을 허용할 것인지 명확히 정의해야 합니다.
파일 보호의 필요성
- 데이터 무결성 유지 : 파일이 권한 없는 사용자에 의해 수정되거나 삭제되는 것을 방지합니다.
- 정보 기밀성 보장 : 중요한 정보가 권한이 없는 사람에게 노출되지 않도록 보호합니다.
파일 권한 설정 방법(모델과 구현)
파일 권한을 설정하는 방법은 이론적인 모델에서부터 실제적인 구현까지 여러 단계로 나뉩니다.
- 이론적 모델 : 접근 제어 행렬 (Access Control Matrix, ACM)
- 파일 보호를 설명하는 이론적인 모델입니다.
- 행(Row)은 ‘누가’(Subject, 사용자, 프로세스)를, 열(Column)은 ‘무엇을’(Object, 파일, 장치)을 나타내는 거대한 행렬입니다. 각 셀(
[사용자 A, 파일 B])에는 해당 사용자가 해당 파일에 대해 갖는 권한(Read,Write등)이 기록됩니다. - 사용자 수 × 파일 수만큼의 거대한 행렬이 필요하므로, 비현실적으로 크고 낭비가 심해 실제 시스템에서 그대로 사용하지 않습니다.
- 실용적 구현 : 접근 제어 목록 (Access Control List, ACL)
- ACM을 현실적으로 구현한 방식입니다. ACM의 ‘열(Column)’을 기준으로, 즉 파일(Object) 을 기준으로 권한 목록을 저장합니다.
- 각 파일마다 “이 파일에 접근할 수 있는 사용자/그룹과 그들의 권한” 목록(ACL)을 붙여둡니다. (
report.docx의 ACL →[(Alice, R/W), (Bob, R), (Admin_Group, R/W/X)]) - 매우 세밀한(fine-grained) 권한 제어가 가능합니다. (Windows NT/XP/Vista/10의 NTFS 파일 시스템이 이 방식을 충실히 따릅니다.)
- 효율적/단순화 구현: UNIX식 권한 (Grouping)
- ACL 방식조차 관리 오버헤드가 크다고 보아, 이를 극도로 단순화하여 효율을 높인 UNIX/Linux의 표준 방식입니다.
- 사용자를 세 가지 범주(Grouping) 로 단순화합니다.
- 소유자 (Owner) : 파일을 생성한 사용자.
- 그룹 (Group) : 소유자가 속한 그룹.
- 기타 (Other) : 위 둘을 제외한 모든 사용자.
- 각 범주에 대해 읽기(Read, 4), 쓰기(Write, 2), 실행(Execute, 1) 세 가지 권한(총 9비트,
rwx rwx rwx)만 설정하도록 제한합니다. - ACL보다 유연성은 떨어지지만, 단 9비트만으로 권한을 표현할 수 있어 매우 효율적이고 관리가 간단합니다. (문서에서 ‘Grouping’으로 설명한 방식)
파일 시스템의 구현(On-Disk Structure)
파일 시스템을 디스크에 설치(포맷)하면, 디스크 파티션(볼륨)은 다음과 같은 논리적인 구조로 조직화됩니다.
- 부트 블록 (Boot Block / Bootstrap) : 시스템 부팅에 필요한 부트로더 코드를 담고 있습니다.
- 슈퍼 블록 (Super Block) : 파일 시스템 전체의 정보를 담고 있는 매우 중요한 메타데이터입니다. (총 블록 수, 빈 블록 수, 파일 시스템 타입, Inode 리스트의 위치 등)
- 자유 공간 관리 (Free-Space Management) : 디스크에서 ‘사용되지 않은(비어있는)’ 데이터 블록이 어디 있는지 추적합니다.
- 비트맵 (Bitmap) : 가장 보편적인 방식. 디스크의 모든 블록을 1:1로 0(사용 중) 또는 1(비어있음)로 표시하는 비트의 배열.
- 메타데이터 영역 (Inode List) : 각 파일의 ‘메타데이터(속성)’를 저장하는 영역입니다.
- 데이터 블록 영역 (Data Blocks) : 실제 파일의 내용(데이터)이 저장되는 영역입니다.
저널링 파일 시스템(Journaling File System)
On-Disk Structure는 치명적인 문제가 있습니다. 만약 OS가 1) 데이터 블록에 데이터를 쓰고, 2) Inode를 갱신하고, 3) 비트맵을 갱신하는 3단계 작업을 하다가, 2단계 직후에 정전되면 파일 시스템이 불일치(Inconsistent) 상태가 됩니다. 데이터는 쓰였지만 메타데이터(Inode, 비트맵)가 갱신되지 않아 디스크가 오염됩니다. (부팅 시 fsck나 CheckDisk가 오래 걸리는 이유)
저널링(Journaling) 은 이 문제를 해결하여 신뢰성(Reliability) 을 보장하는 기법입니다. (NTFS, ext4, APFS 등 모든 현대 FS가 사용)
Write-Ahead Logging: 데이터베이스의 트랜잭션과 매우 유사합니다.
- 작동 방식
- 저널링 (Journaling) : OS는
Inode나Bitmap같은 메타데이터를 수정하기 전에, “내가 이제부터 무슨 작업을 할 것이다”라는 로그(Log) 또는 저널(Journal) 을 디스크의 특별한 영역에 먼저 쓴다. (Write-Ahead) - 커밋 (Commit) : 저널 쓰기가 완료되면, 실제
Inode와Bitmap을 수정하는 본 작업을 수행합니다. - 체크포인트 (Checkpoint) : 본 작업까지 완료되면, 저널에 “이 작업은 완전히 끝났다”고 표시합니다.
- 저널링 (Journaling) : OS는
- 복구 (Recovery)
- 만약 2단계(본 작업) 중에 시스템이 다운되더라도, 부팅 시 OS는 저널만 확인하면 됩니다.
- “로그는 있는데 완료 표시가 없는 작업”을 발견하면, OS는 그 로그를 재실행(Redo) 하여 파일 시스템의 일관성을 즉시 복구합니다.
- 이 덕분에 몇 시간씩 걸리던
fsck가 단 몇 초 만에 끝나게 됩니다.
파일 할당 방식(File Allocation Methods)
“어떻게 파일의 메타데이터와 실제 데이터 블록들을 연결할 것인가?”는 파일 시스템 성능을 결정하는 핵심 문제입니다.
연속 할당(Continuous Allocation)
초기 파일 시스템에서 사용되었으며, 파일 하나를 디스크 상의 연속된 블록에 통째로 저장합니다 (fileA는 10, 11, 12, 13번 블록에 저장). 시작 블록 번호와 파일 크기를 기반으로 데이터의 저장 위치를 관리합니다.
- 빠른 순차/임의 접근 : 디스크 헤드가 한 번의 탐색(Seek)으로 모든 데이터를 읽을 수 있습니다. (매우 빠름)
- 외부 단편화 (External Fragmentation) : 파일을 쓰고 지우다 보면, 연속된 큰 빈 공간이 없어 새 파일을 저장하지 못하는 문제가 발생합니다. (메모리 관리의 연속 할당과 동일한 문제)
- 파일 크기를 예측하기 어려움 : 파일 생성 시 크기를 정해야 하므로 유연성이 떨어집니다.
연결 할당(Linked Allocation)
각 데이터 블록이 다음 데이터 블록을 가리키는 포인터(주소) 를 갖는 방식입니다 (연결 리스트와 동일). 빈 블록 어디에나 저장이 가능하며 파일 크기를 동적으로 늘리기 용이합니다.
- 느린 임의 접근 (Random Access) : 파일의 50번째 블록에 접근하려면, 1번 블록부터 49번까지 포인터를 순차적으로 따라가야 합니다. (매우 느림)
- 신뢰성 : 포인터 하나가 손상되면 파일의 나머지 부분을 모두 잃게 됩니다.
- 포인터 오버헤드 : 각 블록에 포인터 공간이 추가로 필요합니다.
색인 할당(Indexed Allocation)
위 두 방식의 장점을 절충한 방식. 파일마다 ‘인덱스 블록(Index Block)’ 이라는 특별한 블록을 할당합니다. 이 인덱스 블록은 파일의 모든 데이터 블록 주소(포인터) 목록을 저장합니다. (즉, 연결 리스트의 포인터들을 한곳에 모아놓음)
- 빠른 임의 접근: 50번째 블록에 접근하려면, 인덱스 블록에서 50번째 항목(주소)만 읽으면 바로 점프할 수 있습니다.
- 외부 단편화 문제 없음 : 데이터는 임의의 빈 블록에 저장합니다.
- 색인 블록 오버헤드 : 파일이 작아도 인덱스 블록(4KB)이 통째로 필요하므로 공간 낭비가 발생할 수 있습니다.
- 큰 파일의 색인 제한 : 파일이 매우 커지면, 인덱스 블록 하나에 모든 주소를 담지 못할 수 있습니다.
UNIX/Linux의 Inode
이 인덱스 할당의 확장판이 바로 ‘Inode(Index Node)’ 입니다. Inode는 파일의 메타데이터와 함께, 처음 12개 정도의 데이터 블록 주소(직접 블록)와, 더 큰 파일들을 위한 간접 블록(Single/Double/Triple Indirect Block) 의 주소를 저장하여 이 문제를 해결합니다.
디스크 관리(Disk Management)
파일 시스템이 ‘논리적 추상화’라면, 디스크 관리는 그 하부의 ‘물리적 하드웨어’를 다룹니다. 특히 HDD(하드 디스크 드라이브) 는 기계적 부품으로 인해 속도가 매우 느리므로, 이를 최적화하는 것이 중요합니다.
디스크 구조(HDD)
HDD는 여러 장의 플래터(Platter)(LP판)로 구성되며, 헤드(Head)(바늘)가 트랙(Track)(동심원) 위를 이동하며 섹터(Sector)(피자 조각) 단위로 데이터를 읽고 씁니다.
- HDD의 I/O 시간 = 탐색 시간 (Seek Time) + 회전 지연 시간 (Rotational Latency)
- 탐색 시간 (Seek Time) : 헤드가 원하는 트랙까지 이동하는 시간. (I/O 시간 중 가장 큰 비중 차지)
- 회전 지연 시간 : 플래터가 회전하여 원하는 섹터가 헤드 아래에 올 때까지 기다리는 시간.
디스크 스케줄링 (Disk Scheduling)
OS에는 디스크 I/O 요청이 큐(Queue)로 쌓입니다. 이 요청들을 ‘어떤 순서로’ 처리할지 결정하는 것이 디스크 스케줄링이며, 목표는 ‘탐색 시간(Seek Time)’을 최소화하는 것입니다. (CPU 스케줄링의 목표가 응답 시간 최소화인 것과 비교)
(요청 큐 예시: 98, 183, 37, 122, 14, 124, 65, 67 / 현재 헤드 위치: 53)
FCFS(First-Come, First-Served)
먼저 온 요청부터 순서대로 처리합니다.
(53 → 98 → 183 → 37 → 122 …)
- 헤드가 디스크 양 끝을 계속 왕복(Ping-pong)합니다. 매우 비효율적입니다.
SSTF(Shortest Seek Time First)
현재 헤드 위치(53)에서 가장 가까운(탐색 시간이 짧은) 요청부터 처리합니다.
(53 → 65 → 67 → 37 → 14 → 98 …)
- FCFS보다 훨씬 효율적이며 처리율이 높습니다.
- 기아 상태(Starvation) : 헤드가 중앙(60~70)에 머무를 때, 양 끝(1, 190)에 있는 요청은 영원히 처리되지 않을 수 있습니다.
SCAN(Elevator Algorithm)
엘리베이터처럼, 헤드가 한쪽 방향으로 끝까지 이동하면서 경로상의 모든 요청을 처리하고, 끝에 도달하면 방향을 바꿔 돌아옵니다.
(53 → 65 → 67 → 98 → 122 → 124 → 183 → 끝 → 37 → 14)
- SSTF의 기아 상태 문제를 해결하면서도 효율적입니다.
- 헤드 이동이 균형있게 이루어지지만 끝에 있는 요청은 대기 시간이 길어질 수 있습니다.
LOOK (SCAN 최적화)
SCAN과 동일하게 작동하지만, 헤드가 물리적인 디스크의 끝까지 이동하지 않고, 방향 내의 마지막 요청까지만 이동한 뒤 방향을 바꿉니다. (불필요한 헤드 이동 감소)
C-SCAN(Circular SCAN)
SCAN을 개선. 헤드가 한쪽 방향으로만 이동하며 요청을 처리합니다. 끝에 도달하면, 요청을 처리하지 않고 즉시 반대편 끝으로 ‘점프’ 한 뒤, 다시 같은 방향으로 스캔을 시작합니다.
- SCAN보다 대기 시간이 훨씬 더 균일하고 공평합니다. (많은 OS가 채택)
C-LOOK (C-SCAN 최적화)
C-SCAN과 동일하지만,LOOK처럼 방향 내의 마지막 요청까지만 이동한 뒤, 반대편의 첫 번째 요청이 있는 곳으로 점프합니다. (헤드 이동 최소화)
디스크 포맷팅(Formatting)
- 물리적 포맷 (Low-level) : 디스크 표면에 섹터와 트랙을 만드는 공장 단계의 작업.
- 논리적 포맷 (High-level) : 디스크 파티션(볼륨)에 파일 시스템을 설치하는 작업. (
format C: /fs:NTFS). 이 과정을 통해 슈퍼 블록, Inode 테이블, 비트맵 등이 디스크에 생성됩니다.
현대적 고려사항 : SSD(Solid-State Drives)
위의 ‘디스크 관리’는 모두 기계식 HDD를 전제로 한 것입니다. SSD는 기계적 부품(헤드, 플래터)이 없는 플래시 메모리 기반 저장 장치입니다.
- 탐색 시간/회전 지연이 ‘0’ : 모든 블록에 어느 위치든 동일한 속도(O(1)) 로 접근 가능합니다.
- ‘덮어쓰기(Overwrite)’가 불가능합니다. (Erase → Program)
- 셀(Cell)마다 쓰기 수명(P/E Cycle) 이 한정되어 있습니다.
SSD 시대의 디스크 관리(Trade-off)
- 디스크 스케줄링 불필요 : 탐색 시간이 0이므로, SSTF나 SCAN, C-SCAN 알고리즘은 아무 의미가 없습니다. (오히려 FCFS가 공평하고 효율적)
- 새로운 관리 문제 : OS와 SSD 펌웨어(FTL)는 ‘마모 평준화(Wear Leveling)’(모든 셀이 골고루 닳도록 쓰기 작업을 분산)와 ‘가비지 컬렉션(Garbage Collection)’(지워진 블록을 정리)을 수행해야 합니다.
- TRIM : OS가 “이 블록은 삭제되었으니 더 이상 유지할 필요 없다”라고 SSD에 알려주는 명령어로, 가비지 컬렉션 효율을 높여 SSD의 속도 저하를 방지합니다.