시스템 · 서버기초 · 16분 읽기

Linux 파일과 경로 읽기

파일 이름, 실제 파일 객체, 마운트 지점을 구분하면 경로 문제와 저장 위치를 정확히 추적할 수 있습니다.

개념부터 찾아보기

정의·역할·동작 원리를 독립 페이지에서 확인하세요. 본문의 밑줄 용어에도 설명이 있습니다.

이 노트에서 설명할 수 있게 되는 것

  1. 경로 탐색에서 디렉터리·inode·열린 파일의 역할을 구분한다.
  2. 하드 링크·심볼릭 링크·마운트가 이름 해석에 미치는 영향을 설명한다.
  3. 경로 오류와 권한 오류, 삭제 후 공간 점유를 관찰 결과로 구분한다.

01디렉터리 항목, inode, 데이터 블록은 서로 다른 객체다

  • 일반적인 Unix 파일 시스템에서 디렉터리는 파일 이름을 에 연결한다.
  • 에는 파일 종류, 소유 UID와 GID, 접근 모드, 크기, 시간 정보, 와 데이터 위치를 찾기 위한 정보가 들어간다. 파일 이름 자체를 안에 하나만 저장하는 구조가 아니므로 서로 다른 두 이름이 같은 파일 객체를 가리킬 수 있다.
  • 는 해당 파일 시스템 안에서만 의미가 있다.
    • 다른 장치의 가 같다고 같은 파일은 아니다.
  • 가 /srv/lesson/report.txt를 열면 커널은 /부터 srv, lesson을 차례로 탐색하고 마지막 에서 report.txt의 객체를 찾는다.
  • 중간 요소에 심볼릭 링크나 마운트 지점이 있으면 탐색 대상이 다른 경로 또는 파일 시스템으로 바뀐다.
  • 는 이 과정을 공통 인터페이스로 제공하며 실제 디스크 배치는 ext4, XFS 등 파일 시스템 구현이 맡는다.
  • /dev의 장치 파일, 일반 파일, 디렉터리, 소켓은 유형과 사용 방식이 다르다.
  • 확장자는 이름의 일부이므로 확장자만으로 실행 가능 여부, 실제 형식, 문자 인코딩을 확정하지 않는다.
bash
ls -li report.txt
stat report.txt
# 예시: inode 42001, links 1, size 12
# 파일 이름과 inode 번호를 함께 관찰한다.

교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.

02하드 링크와 심볼릭 링크를 같은 실험으로 비교하기

  • 는 동일한 에 새 을 추가한다.
  • 두 이름 사이에는 원본과 복사본이라는 구별이 없으며 한쪽으로 내용을 바꾸면 다른 쪽에서도 같은 내용이 보인다.
  • 일반 파일의 는 같은 파일 시스템 안에서 만들며, 일반 사용자가 디렉터리를 로 연결하는 방식은 허용되지 않는다.
  • 는 대상 경로 문자열을 담는 별도 객체다.
    • 대상 파일이 없어져도 링크 객체는 남아 끊어진 링크가 된다.
  • 상대 경로를 담은 는 링크가 놓인 디렉터리를 기준으로 해석하므로 현재 셸 디렉터리와 혼동하면 안 된다.
  • 아래 코드는 새 실습 디렉터리에서만 수행하는 관계 비교 예시다.
    • 링크 생성은 데이터 복사와 다르다.
bash
printf 'alpha\n' > note.txt
ln note.txt note-hard.txt
ln -s note.txt note-link.txt
ls -li note.txt note-hard.txt note-link.txt
# note.txt와 note-hard.txt: inode 같음, link count 2
# note-link.txt: inode 다름, note.txt라는 경로를 참조

교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.

03open, read, close와 삭제 후에도 남는 데이터

  • open이 성공하면 는 정수인 를 받는다.
  • 0, 1, 2는 관례적으로 표준 입력·출력·오류이고, 새 파일은 사용 가능한 번호를 배정받는다.
  • 디스크립터는 커널의 을 통해 현재 읽기·쓰기 위치와 접근 모드 등을 참조한다.
  • read는 현재 위치에서 데이터를 가져와 위치를 이동시키고, close는 해당 의 참조를 해제한다.
    • 이름으로 다시 찾는 동작과 이미 연 파일을 읽는 동작은 다르다.
  • unlink는 디렉터리의 이름을 제거한다.
  • 마지막 를 지웠어도 가 파일을 열고 있으면 데이터 객체는 계속 사용될 수 있다.
    • 따라서 삭제된 로그 파일을 프로그램이 계속 쓰면 디렉터리 목록에서는 사라졌지만 공간은 바로 회수되지 않는다.
  • 와 열린 참조가 모두 사라질 때 저장 공간을 회수할 수 있다.
  • 같은 원리로 실행 파일을 교체해도 이미 실행 중인 가 이전 객체를 계속 사용하는 경우가 있다.
bash
ls -l /proc/1234/fd
readlink /proc/1234/exe
lsof +L1
# 1234는 관찰할 프로세스 PID로 바꾼다.
# (deleted)는 이름이 제거된 열린 객체일 수 있다.

교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.

원리 심화

04디스크립터·열린 파일 설명·inode의 공유 범위

  • 의 fd 테이블은 작은 정수 번호를 에 연결한다. 에는 현재 오프셋·파일 상태 플래그가 있고 그 아래 파일 객체가 연결된다.
  • 같은 경로를 두 번 open하면 보통 서로 다른 을 얻는다. 같은 파일의 내용은 공유하지만 두 오프셋은 독립적으로 움직인다.
  • dup로 복제한 fd와 로 상속한 fd는 같은 을 참조한다. 한쪽의 read나 lseek가 바꾼 오프셋을 다른 쪽에서도 보게 된다.
  • O_APPEND 같은 파일 상태 플래그는 에 속한다. FD_CLOEXEC는 fd별 속성이므로 같은 설명을 공유하는 두 번호의 설정이 다를 수 있다.
  • 는 프로그램을 교체하지만 닫힘 표시가 없는 fd는 새 프로그램으로 이어질 수 있다. O_CLOEXEC는 열 때 이 전달을 제한하는 의도를 함께 지정한다.
예제로 추적

05ABCDEF를 두 참조로 읽을 때의 오프셋 추적

  • 내용이 ASCII 바이트 ABCDEF인 일반 파일을 읽기 모드로 열어 fd 3을 얻고 dup(3)이 fd 4를 반환했다고 가정한다. 처음 공유 오프셋은 0이다.
  • fd 3에서 2바이트를 읽으면 AB를 얻고 공유 오프셋은 2가 된다. 파일 내용이 두 바이트 줄어든 것이 아니다.
  • fd 4에서 2바이트를 읽으면 CD를 얻고 오프셋은 4가 된다. 새 번호라는 이유로 처음부터 읽는 것은 아니다.
  • 같은 경로를 새로 open한 fd 5는 별도 오프셋 0에서 시작한다. fd 5의 2바이트 읽기는 AB이며 fd 3·4의 위치 4는 그대로다.
  • close(3) 뒤에도 fd 4의 참조가 남으면 공유 설명은 계속 사용된다. 이후 read(4,2)는 EF를 읽는다.
호출 후fd 3·4 공유 위치fd 5 위치
초기0아직 없음
read(3,2)2아직 없음
read(4,2)4아직 없음
open→read(5,2)42
검증·예외

06경로 오류와 열린 파일 오류를 분리해 읽기

  • 구성 요소가 없거나 의 대상이 없으면 이름 탐색이 실패한다. 화면에 링크 이름이 보인다는 것만으로 최종 파일의 존재가 증명되지 않는다.
  • a/b/c를 열 때 b가 일반 파일이면 중간 요소를 디렉터리로 통과할 수 없다. 최종 c의 권한을 바꾸어 해결되는 문제가 아니다.
  • 경로의 디렉터리 탐색 권한 또는 최종 파일 접근 권한에서 거부될 수 있다. 대상 파일의 mode만 읽지 말고 경로 각 요소의 실행 주체를 대입한다.
  • EMFILE은 가 열 수 있는 fd 한계, ENFILE은 시스템의 열린 파일 한계와 관계한다. 디스크 여유 공간 부족과 다른 자원 문제다.
  • 같은 이름을 재생성했는데 서비스 출력이 보이지 않으면 열린 fd의 대상과 현재 경로의 를 비교한다. 이름이 같아도 기존 서비스가 삭제된 이전 객체를 계속 쓸 수 있다.
bash
ls -li /srv/lesson/report.txt
stat /srv/lesson/report.txt
readlink /proc/1234/fd/3

교육용 조회 예시. PID 1234와 경로는 가상 값이며 접근 가능한 에 한해 해석한다.

07mount가 디렉터리 트리의 의미를 바꾸는 방법

  • 마운트는 장치 이름을 디렉터리 별명으로 바꾸는 작업이 아니라 파일 시스템의 루트를 기존 디렉터리 트리의 특정 위치에 연결하는 작업이다.
  • /srv/data에 파일이 있는 상태에서 다른 파일 시스템을 그곳에 마운트하면 원래 파일들은 삭제되지 않고 아래에 가려진다.
    • 마운트를 해제하면 다시 보인다.
  • 장치가 마운트되지 않은 채 서비스가 /srv/data에 파일을 쓰면 예상한 데이터 디스크가 아니라 상위 루트 파일 시스템이 가득 찰 수 있다.
  • df는 마운트된 파일 시스템의 사용량을, du는 접근 가능한 을 순회하며 사용량을 추정한다.
    • 열린 삭제 파일, 가려진 파일, 예약 공간과 희소 파일 등 때문에 값이 항상 일치하지 않는다.
  • findmnt -T는 특정 경로를 실제로 담당하는 마운트를 찾고 lsblk는 블록 장치 계층을 보여 준다.
    • 따라서 경로, 마운트, 장치 세 가지 이름을 분리해서 읽어야 한다.
bash
findmnt -T /srv/data
lsblk -f
df -hT /srv/data
du -sh /srv/data

교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.

08셸이 해석한 문자열과 프로그램이 받는 경로

  • 절대 경로는 루트 디렉터리 /에서 시작하고, 상대 경로는 현재 작업 디렉터리에서 시작한다.
  • 같은 상대 경로라도 프로그램의 실행 위치가 달라지면 다른 파일을 가리킨다.
  • ~는 일반적인 셸에서 홈 디렉터리로 확장되는 표현이며 모든 프로그램이 직접 해석하는 파일 시스템 문법은 아니다.
  • 셸은 명령을 실행하기 전에 변수 확장, 따옴표 처리, 와일드카드 확장 등을 수행한다.
  • report*.txt를 따옴표 없이 전달하면 셸이 현재 디렉터리에서 일치하는 여러 이름으로 바꿀 수 있지만, "report*.txt"는 별표가 포함된 문자열 하나다.
  • 공백이 있는 경로를 인용하지 않으면 여러 인자로 나뉠 수 있다.
    • 파일 시스템은 셸의 이런 편의 문법을 직접 해석하지 않는다.
  • 리다이렉션도 셸의 일이다.
  • >는 표준 출력을 지정 파일로 연결하면서 기존 내용을 덮어쓸 수 있고, >>는 끝에 덧붙인다.
  • 파이프는 앞 의 표준 출력을 뒤 의 표준 입력으로 연결한다.
  • cat과 less는 파일 읽기 도구이지만 find는 디렉터리 트리를 탐색한다.
  • 이름·내용·메타데이터 중 무엇을 검색하는지에 따라 도구를 고른다.
bash
printf "%s\n" "reports/final note.txt"
find /usr/share/doc -maxdepth 1 -type d
# 문자열 하나와 디렉터리 항목 검색을 구분한다.

교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.

09조회 결과를 연결해서 해석하기

  • 먼저 현재 위치와 대상 유형을 확인하고, 그 경로가 어느 파일 시스템에 있는지 확인한다.
  • 조회 결과의 크기·소유자·시간은 서로 다른 의미이므로 한 숫자만으로 파일의 상태를 판단하지 않는다.
bash
pwd
ls -ld .
stat .
findmnt -T .

현재 디렉터리의 경로·메타데이터·마운트 관계를 조회한다.

10하나의 파일을 열 때 생기는 객체

  • 작업 디렉터리가 /srv인 프로그램이 notes/day.txt를 읽는 상황을 생각한다.
  • 일반적인 상대 경로 열기에서는 /srv에서 notes를 찾고, 그 디렉터리 안에서 day.txt를 찾는다.
  • 중간 구성 요소는 디렉터리여야 하며 각 단계의 탐색 권한이 필요하다.
  • notes가 다른 파일 시스템의 마운트 지점이면 그 지점부터 연결된 파일 시스템에서 다음 이름을 찾는다.
    • 따라서 문자열의 모양만으로 실제 저장 장치를 확정할 수 없다.
  • 이름 해석으로 찾은 파일의 접근 조건을 만족하면 커널은 열린 파일 상태와 이를 가리키는 를 제공한다.
  • 프로그램의 read는 매번 전체 경로를 다시 해석하는 대신 이 열린 참조를 사용한다.
  • 열린 파일 상태에는 읽기 위치와 접근 모드 등이 관계한다.
  • 파일 이름은 , 는 파일 객체의 정보, 디스크립터는 해당 에서 사용하는 참조이므로 세 값을 동일한 식별자로 생각하면 안 된다.
  1. read 전용으로 notes.txt를 여는 경우를 가정한다. /로 시작하지 않는 경로이므로 현재 작업 디렉터리에서 탐색을 시작한다.
  2. 경로의 중간 디렉터리와 마운트 경계를 따라 최종 이름이 가리키는 파일을 찾는다. 중간 디렉터리의 탐색 권한도 필요하다.
  3. 파일 유형과 요청한 읽기 모드, 접근 정책을 검사한다. 존재하지 않는 이름이나 권한 거부는 열기 실패로 반환된다.
  4. 성공하면 열린 파일 상태에 접근 모드와 현재 위치 등을 연결하고, 가 사용할 를 반환한다.
  5. read는 이미 열린 참조를 이용하며 읽은 만큼 파일 위치가 변한다. 사용 후 close로 해당 디스크립터의 참조를 해제한다.

설명과 함께 보는 보조 도식

처리 과정

상대 경로에서 열린 파일까지

기존 일반 파일을 읽기 모드로 여는 성공 경로다.

전체 단계를 한 번에 읽기
  1. 01

    기준 선택

    /로 시작하지 않으므로 현재 작업 디렉터리에서 출발한다.

    기준
    /srv
  2. 02

    이름 탐색

    중간 디렉터리와 마운트를 따라 최종 이름을 해석한다.

    대상
    notes/day.txt
  3. 03

    접근 판정

    파일 유형과 요청한 읽기 권한을 검사한다.

    요청
    read
  4. 04

    열린 참조 생성

    읽기 위치와 모드를 가진 열린 파일 상태를 연결한다.

    초기 위치
    파일 시작
  5. 05

    참조로 읽기

    반환된 디스크립터를 이용해 읽고 사용 후 닫는다.

    경로 재탐색
    일반적인 read에서는 불필요
학습 모식도. 표시한 조건에서의 객체와 상태 변화를 설명하며 운영 환경의 모든 분기와 내부 구현은 생략한다.

11이름이 사라졌는데 공간이 남는 이유

  • 삭제된 로그에 열린 참조가 남으면 디렉터리를 합산한 du와 파일 시스템 전체 사용량인 df가 다르게 보일 수 있다.
  • 단순히 두 도구 중 하나가 잘못됐다고 판단하지 않고 앞에서 구분한 와 열린 참조를 대조한다.
  • 경로를 조사할 때는 현재 작업 디렉터리, 각 중간 구성 요소의 유형과 권한, 링크 대상, 최종 마운트를 순서대로 대조한다.
  • 존재하지 않는 구성 요소, 디렉터리가 아닌 중간 파일, 통과 권한 부족은 서로 다른 실패다.
  • 같은 이름을 삭제하고 새 파일을 만들었다면 기존에 열린 참조와 새 경로가 다른 파일을 가리킬 수도 있다.
  • 로그 교체 뒤 가 어느 파일에 쓰는지 확인해야 하는 이유가 여기에 있다.
구성·관찰 대상확인하는 정보구분할 문제
이름과 파일 객체의 연결이름 변경·삭제·끊어진 링크
유형·소유권·이름은 달라도 같은 파일인지
열린 파일 참조접근 모드와 읽기 위치이름 삭제 후에도 남은 사용
마운트경로와 파일 시스템의 연결다른 장치의 파일이 보이는지
학습 확인

이해했는지 확인하기

  • 상대 경로의 기준 디렉터리를 확인한다.
  • 심볼릭 링크와 실제 대상의 상태를 구분한다.
  • 파일 경로와 실제 저장 장치의 연결을 확인한다.

함께 확인할 공식 자료