Skip to content
난이도 문제은행 3

파일 시스템 ​

파일 시스템(file system)은 디스크의 번호 붙은 블록(block) 더미를 이름 붙은 파일과 폴더로 보이게 해 주는 운영체제의 부분이다. 프로그램은 "몇 번 블록"이 아니라 /home/me/a.txt 같은 경로로 파일을 열고, 열린 파일은 파일 디스크립터(file descriptor)라는 작은 정수로 다룬다. 유닉스 계열에서는 파일의 내용·정보를 담는 inode(index node, 색인 노드)와 파일 이름이 분리돼 있어서, 한 파일에 이름을 여러 개 붙이는 링크와 이름 바꾸기의 동작이 이 구조에서 나온다.

파일과 디렉터리 ​

파일은 이름이 붙은 바이트의 모음이다. 파일 시스템은 내용 외에 크기·소유자·권한·수정 시각 같은 메타데이터(metadata)도 함께 관리한다. 디렉터리(directory)는 "이름 → 파일" 대응을 적어 둔 특별한 파일이다. 디렉터리 안에 디렉터리를 넣을 수 있으므로 전체가 /에서 시작하는 계층 구조가 되고, 경로는 그 계층을 따라 내려가는 이름의 나열이다.

파일 디스크립터 ​

프로세스(process)는 open() 시스템 콜(system call)로 파일을 연다. 커널(kernel)은 경로를 따라가 파일을 찾고 권한을 확인한 뒤, 그 프로세스 안에서만 통하는 작은 정수를 돌려준다. 이것이 파일 디스크립터다. 이후 read()·write()는 경로 대신 이 번호로 파일을 가리키고, 다 쓰면 close()로 번호를 반납한다. 매번 경로를 다시 찾지 않아도 되고, 커널이 "어디까지 읽었는지" 같은 상태를 번호 뒤에서 대신 기억해 준다.

새로 여는 파일은 그 프로세스에서 지금 비어 있는 가장 작은 번호를 받는다. 0·1·2번은 프로세스가 시작할 때부터 열려 있다.

번호이름기본 연결
0표준 입력(stdin)키보드
1표준 출력(stdout)터미널 화면
2표준 오류(stderr)터미널 화면

셸(shell, 명령을 입력받아 실행해 주는 프로그램)에서 cmd > out.txt 2>&1이라고 쓰면, 앞의 > out.txt는 1번을 out.txt 파일로 바꾸고 뒤의 2>&1은 2번도 1번이 가리키는 곳으로 보낸다는 뜻이다. 유닉스 계열에서는 일반 파일뿐 아니라 파이프(pipe)와 소켓(socket)도 파일 디스크립터로 다룬다. 그래서 같은 read()·write()로 파일·프로세스 간 통신·네트워크를 모두 다룰 수 있다. fork()(지금 프로세스를 복사해 자식 프로세스를 만드는 시스템 콜)로 만든 자식은 부모의 파일 디스크립터를 물려받고, 같은 파일의 읽기 위치도 함께 쓴다. 그래서 부모가 100바이트를 읽으면 자식은 그다음 바이트부터 읽는다.

파일 할당 방식 ​

파일 시스템은 파일 내용을 디스크 블록(disk block) 여러 개에 나눠 담는다. 어느 블록들이 한 파일인지 기록하는 방법은 연속 할당(contiguous allocation), 연결 할당(linked allocation), 색인 할당(indexed allocation) 세 가지다.

방식기록하는 것장점단점
연속 할당시작 블록과 길이순차·임의 접근 모두 빠름빈 공간이 조각남, 파일 늘리기 어려움
연결 할당블록마다 다음 블록 번호빈 블록 아무 데나 사용중간 블록을 읽으려면 처음부터 따라감
색인 할당블록 번호 목록을 담은 색인 블록임의 접근이 빠름색인 블록만큼 공간이 더 듦

연속 할당은 흩어진 빈 공간을 모아 쓰지 못하는 외부 단편화(external fragmentation)가 생긴다. 연결 할당은 이 문제가 없지만, 파일 중간을 읽으려면 앞 블록들을 차례로 따라가야 한다. 연결 할당의 흔한 변형인 FAT(File Allocation Table, 파일 할당 표)은 "다음 블록 번호"들을 블록 안이 아니라 디스크 앞쪽의 표 하나에 모은다. 표를 메모리에 올려 두면 디스크를 읽지 않고도 블록 사슬을 따라갈 수 있다. USB 메모리 등에서 쓰는 FAT32·exFAT이 이 계열이다.

유닉스 계열의 inode는 색인 할당을 쓴다. inode 안에 데이터 블록을 직접 가리키는 포인터(pointer)가 몇 개 있고, 파일이 커지면 "블록 번호 목록이 담긴 블록"을 가리키는 간접 포인터를 쓴다. 그래도 모자라면 그런 목록 블록들의 번호를 담은 블록을 한 겹 더 두는 식으로 이중·삼중 간접 포인터까지 늘린다. 작은 파일은 직접 포인터만으로 빨리 찾고, 큰 파일도 표현할 수 있다. 리눅스 ext4는 블록을 하나씩 적는 대신 연속된 블록 구간을 '시작 블록과 길이'로 한 번에 적는 extent(연속 구간) 방식을 쓴다. 큰 파일이 연속으로 놓여 있으면 적어야 할 항목 수가 크게 준다.

inode와 파일 이름의 분리 ​

inode에는 파일의 종류·크기·소유자·권한·시각, 데이터 블록 위치, 그리고 이 inode를 가리키는 이름의 개수인 링크 수가 들어 있다. 파일 이름은 inode에 없다. 이름은 디렉터리 항목에 "이름 → inode 번호" 형태로만 있다. 그래서 같은 파일 시스템 안에서 이름을 바꾸거나 옮기는 것은 디렉터리 항목만 고치면 되므로, 파일이 아무리 커도 순식간에 끝난다. 반면 다른 파일 시스템으로 옮기면 inode 번호가 통하지 않으므로 데이터를 실제로 복사한 뒤 원본을 지워야 한다.

이어지는 내용은 파일 시스템 2에서 다룬다.

면접 포인트 ​

  • "파일 디스크립터란?" → 프로세스가 연 파일을 가리키는 작은 정수. 0·1·2는 표준 입력·출력·오류. 파이프·소켓도 같은 번호로 다룬다.
  • "파일 할당 방식의 차이는?" → 연속(빠르지만 외부 단편화), 연결(아무 블록이나 쓰지만 중간 접근이 느림, FAT은 다음 번호를 표에 모음), 색인(inode의 직접·간접 포인터).
  • "inode란?" → 파일의 메타데이터와 데이터 블록 위치를 담는 구조. 이름은 inode가 아니라 디렉터리 항목에 있다.
  • "큰 파일도 이름 바꾸기가 순식간인 이유는?" → 같은 파일 시스템 안에서는 디렉터리 항목의 "이름 → inode 번호"만 고치면 되기 때문.

관련 문서