본문 바로가기
TYLER SONGBlog
← Blog index
CS학습7 min read

Linux 파일 시스템: 모든 것을 하나의 디렉터리 트리와 inode로 다루는 구조

Linux 파일 시스템은 루트(/)에서 시작하는 단일 디렉터리 트리 아래에 파일, 장치, 프로세스 정보까지 "파일"이라는 인터페이스로 통합해 다룬다. 파일의 실제 메타데이터는 inode에 저장되고, 디렉터리는 이름과 inode 번호를 연결하는 표 역할을 한다. 이 글에서는 디렉터리 구조, inode, 링크, 마운트, 권한을 Python과 Shell 예제로

By 송민성

1. 개념

파일 시스템(file system)은 저장 장치의 블록(block)을 파일과 디렉터리라는 단위로 추상화해 이름으로 데이터를 저장하고 찾을 수 있게 하는 운영체제 구성 요소다.

Linux는 두 가지 특징을 가진다.

  • 단일 디렉터리 트리: Windows의 C:, D: 같은 드라이브 문자 대신, 루트(root) 디렉터리 / 하나에서 모든 경로가 시작한다. 다른 디스크나 파티션은 트리의 특정 디렉터리에 마운트(mount)해 붙인다.
  • "모든 것은 파일"이라는 추상화: 일반 파일, 디렉터리, 장치(/dev/sda), 프로세스 정보(/proc) 등을 open, read, write, close 같은 같은 시스템 콜 인터페이스로 다룬다.

주요 디렉터리는 파일 시스템 계층 표준(FHS, Filesystem Hierarchy Standard)에 따라 구성된다.

경로용도
/최상위 루트 디렉터리
/etc시스템 설정 파일
/home일반 사용자의 홈 디렉터리
/var로그, 캐시 등 자주 변하는 데이터
/tmp임시 파일
/usr설치된 프로그램과 라이브러리
/bin, /sbin기본 실행 파일 (많은 배포판에서 /usr/bin 등으로의 심볼릭 링크)
/dev장치 파일
/proc, /sys커널과 프로세스 정보를 노출하는 가상 파일 시스템

2. 왜 사용하는가

  • 통일된 접근 방식: 디스크 파일이든 터미널이든 같은 read/write로 다루므로 프로그램이 단순해진다. 셸의 파이프와 리다이렉션도 이 추상화 덕분에 동작한다.
  • 저장 장치와 경로의 분리: 애플리케이션은 /var/log/app.log만 알면 되고, 이 경로가 어느 디스크에 있는지는 마운트 설정이 결정한다.
  • 권한과 소유자 관리: 파일마다 소유자(owner), 그룹(group), 권한 비트를 두어 다중 사용자 환경에서 접근을 제어한다.
  • 다양한 파일 시스템 공존: ext4, XFS, Btrfs, tmpfs, NFS 등을 VFS(Virtual File System) 계층 덕분에 같은 방식으로 사용할 수 있다.

3. 동작 원리

inode와 디렉터리 엔트리

대부분의 Unix 계열 파일 시스템(ext4 등)은 파일의 메타데이터를 inode에 저장한다. inode에는 파일 종류, 권한, 소유자, 크기, 타임스탬프, 데이터 블록 위치 등이 들어 있다. 파일 이름은 inode에 들어 있지 않다.

디렉터리는 "이름 → inode 번호" 매핑을 담은 특수한 파일이다. 따라서 /home/user/a.txt를 여는 과정은 대략 다음과 같다.

  1. 루트 디렉터리의 inode를 읽고, 그 내용에서 home의 inode 번호를 찾는다.
  2. home 디렉터리에서 user의 inode 번호를 찾는다.
  3. user 디렉터리에서 a.txt의 inode 번호를 찾는다.
  4. 해당 inode가 가리키는 데이터 블록을 읽는다.

하드 링크와 심볼릭 링크

  • 하드 링크(hard link): 같은 inode를 가리키는 또 다른 이름이다. inode에는 링크 수(link count)가 있고, 이 값이 0이 되며 더 이상 열려 있지 않을 때 데이터가 해제된다. 같은 파일 시스템 안에서만 만들 수 있고, 디렉터리에는 일반적으로 만들 수 없다.
  • 심볼릭 링크(symbolic link): 대상 경로 문자열을 담은 별도의 파일이다. 다른 파일 시스템도 가리킬 수 있고, 대상이 삭제되면 끊어진 링크(dangling link)가 된다.

마운트와 VFS

VFS는 여러 파일 시스템 구현 위에 공통 인터페이스를 제공하는 커널 계층이다. mount 명령은 특정 장치의 파일 시스템을 트리의 한 디렉터리(마운트 포인트)에 연결한다. 그 이후 해당 경로 아래의 접근은 연결된 파일 시스템이 처리한다.

권한

ls -l의 -rwxr-xr--는 파일 종류(1글자)와 소유자/그룹/기타 사용자 각각의 읽기(r=4), 쓰기(w=2), 실행(x=1) 권한을 나타낸다. 위 예시는 숫자로 754다.

4. 코드 예제

Shell: inode와 링크 확인

shell
mkdir -p /tmp/fs-demo && cd /tmp/fs-demo echo "hello" > original.txt ln original.txt hard.txt # 하드 링크 ln -s original.txt soft.txt # 심볼릭 링크 ls -li # 첫 열이 inode 번호 stat original.txt # Inode, Links, Access 등 확인 rm original.txt cat hard.txt # 하드 링크는 여전히 읽힘 cat soft.txt # 대상이 사라져 오류 발생

original.txt와 hard.txt는 inode 번호가 같고, soft.txt는 다른 inode를 가진다. 원본을 지워도 링크 수가 남아 있으므로 hard.txt는 정상적으로 읽힌다.

Shell: 권한 변경과 디스크 사용량

shell
touch /tmp/fs-demo/script.sh chmod 754 /tmp/fs-demo/script.sh ls -l /tmp/fs-demo/script.sh df -h / # 파일 시스템별 용량 df -i / # inode 사용량 du -sh /tmp/fs-demo

Python: inode 정보를 프로그램에서 읽기

python
import os import stat import tempfile with tempfile.TemporaryDirectory() as d: src = os.path.join(d, "original.txt") hard = os.path.join(d, "hard.txt") soft = os.path.join(d, "soft.txt") with open(src, "w") as f: f.write("hello\n") os.link(src, hard) # 하드 링크 os.symlink(src, soft) # 심볼릭 링크 s_src = os.stat(src) s_hard = os.stat(hard) s_soft = os.lstat(soft) # 링크 자체의 정보 print("원본 inode:", s_src.st_ino, "링크 수:", s_src.st_nlink) print("하드 링크 inode:", s_hard.st_ino) print("심볼릭 링크 inode:", s_soft.st_ino) print("같은 inode인가:", s_src.st_ino == s_hard.st_ino) print("심볼릭 링크인가:", stat.S_ISLNK(s_soft.st_mode)) print("권한:", oct(stat.S_IMODE(s_src.st_mode))) os.remove(src) with open(hard) as f: print("원본 삭제 후 hard 읽기:", f.read().strip()) print("soft 경로 존재 여부:", os.path.exists(soft)) # 끊어진 링크는 False

5. 시간 복잡도 또는 성능 특성

파일 시스템 성능은 구현과 하드웨어에 크게 좌우되므로 단일한 시간 복잡도로 말하기 어렵다. 일반적으로 확인할 수 있는 특성은 다음과 같다.

  • 경로 탐색 비용: 경로 깊이만큼 디렉터리 조회가 필요하다. 커널은 dentry 캐시와 inode 캐시로 반복 조회를 줄인다.
  • 페이지 캐시(page cache): 읽은 데이터는 메모리에 캐시되어, 같은 파일을 다시 읽을 때 디스크 접근 없이 처리되는 경우가 많다.
  • 쓰기 지연: write()는 보통 페이지 캐시에 기록하고 반환한다. 디스크 반영을 보장하려면 fsync()가 필요하다.
  • 저널링(journaling): ext4 같은 저널링 파일 시스템은 변경 내용을 먼저 저널에 기록해 비정상 종료 후 메타데이터 일관성을 복구하기 쉽게 한다.
  • inode 개수 제한: ext4는 포맷 시점에 inode 개수가 정해지므로, 작은 파일이 매우 많으면 용량이 남아도 inode가 고갈될 수 있다.

6. 실무 사용 사례

  • 로그 관리: /var/log 아래 로그가 디스크를 채우면 서비스 장애로 이어진다. df -h와 du -sh /var/log/*로 점검하고 logrotate로 순환한다.
  • 배포 전환: 릴리스 디렉터리를 /srv/app/releases/20261024처럼 만들고 current 심볼릭 링크를 새 릴리스로 교체하면 빠르게 전환하고 되돌릴 수 있다.
  • 컨테이너 볼륨: Docker 볼륨과 bind mount는 호스트의 디렉터리를 컨테이너 파일 시스템 트리에 마운트하는 방식이다.
  • 디스크 추가: 새 디스크를 mkfs로 포맷한 뒤 /data에 마운트하고, /etc/fstab에 등록해 재부팅 후에도 유지한다.
  • 프로세스 진단: /proc/<pid>/fd로 프로세스가 연 파일 목록을, /proc/meminfo로 메모리 정보를 확인한다.

7. 주의할 점

  • `rm -rf` 사용: 휴지통 없이 즉시 삭제된다. 변수가 비어 rm -rf "$DIR"/가 / 하위로 확장되는 실수를 조심한다.
  • 디스크는 남는데 쓰기가 실패하는 경우: df -i로 inode 고갈 여부를 확인한다.
  • 삭제했는데 용량이 줄지 않는 경우: 삭제된 파일을 프로세스가 아직 열고 있으면 공간이 해제되지 않는다. lsof | grep deleted로 확인하고 프로세스를 재시작한다.
  • 심볼릭 링크 혼동: 상대 경로로 만든 심볼릭 링크는 링크 파일 위치 기준으로 해석된다. 링크를 옮기면 끊어질 수 있다.
  • 권한 `777` 남용: 모든 사용자에게 쓰기를 허용하므로 보안상 위험하다. 필요한 최소 권한만 부여한다.
  • fstab 오류: /etc/fstab을 잘못 수정하면 부팅 실패로 이어질 수 있다. 수정 후 mount -a로 먼저 검증한다.
  • 임시 파일 생성: 예측 가능한 이름으로 /tmp에 파일을 만들면 보안 문제가 생길 수 있으므로 mktemp나 Python의 tempfile을 사용한다.

8. 핵심 정리

  • Linux는 /에서 시작하는 단일 디렉터리 트리를 사용하며, 다른 저장 장치는 마운트로 연결한다.
  • 파일의 메타데이터는 inode에 있고, 파일 이름은 디렉터리의 "이름 → inode 번호" 매핑에 있다.
  • 하드 링크는 같은 inode를 가리키는 이름, 심볼릭 링크는 경로 문자열을 담은 별도 파일이다.
  • VFS 덕분에 ext4, XFS, tmpfs, NFS 등 서로 다른 파일 시스템을 같은 시스템 콜로 다룰 수 있다.
  • 권한은 소유자/그룹/기타 각각의 r(4), w(2), x(1) 조합으로 표현한다.
  • 운영 중에는 df -h, df -i, du, stat, ls -li로 용량과 inode 상태를 점검한다.