시스템 · 서버기초 · 16분 읽기
Linux 파일과 경로 읽기
파일 이름, 실제 파일 객체, 마운트 지점을 구분하면 경로 문제와 저장 위치를 정확히 추적할 수 있습니다.
개념부터 찾아보기
정의·역할·동작 원리를 독립 페이지에서 확인하세요. 본문의 밑줄 용어에도 설명이 있습니다.
이 노트에서 설명할 수 있게 되는 것
- 경로 탐색에서 디렉터리·inode·열린 파일의 역할을 구분한다.
- 하드 링크·심볼릭 링크·마운트가 이름 해석에 미치는 영향을 설명한다.
- 경로 오류와 권한 오류, 삭제 후 공간 점유를 관찰 결과로 구분한다.
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와 삭제 후에도 남는 데이터
- openopen이 성공하면 는 정수인 를 받는다.
- 0, 1, 20, 1, 2는 관례적으로 표준 입력·출력·오류이고, 새 파일은 사용 가능한 번호를 배정받는다.
- 열린 파일디스크립터는 커널의 을 통해 현재 읽기·쓰기 위치와 접근 모드 등을 참조한다.
- readread는 현재 위치에서 데이터를 가져와 위치를 이동시키고, close는 해당 의 참조를 해제한다.
- 이름으로 다시 찾는 동작과 이미 연 파일을 읽는 동작은 다르다.
- unlink·디렉터리unlink는 디렉터리의 이름을 제거한다.
- 마지막 를 지웠어도 가 파일을 열고 있으면 데이터 객체는 계속 사용될 수 있다.
- 따라서 삭제된 로그 파일을 프로그램이 계속 쓰면 디렉터리 목록에서는 사라졌지만 공간은 바로 회수되지 않는다.
- 와 열린 참조가 모두 사라질 때 저장 공간을 회수할 수 있다.
- 실행 파일 교체같은 원리로 실행 파일을 교체해도 이미 실행 중인 가 이전 객체를 계속 사용하는 경우가 있다.
bash
ls -l /proc/1234/fd
readlink /proc/1234/exe
lsof +L1
# 1234는 관찰할 프로세스 PID로 바꾼다.
# (deleted)는 이름이 제거된 열린 객체일 수 있다.교육용 예시. 표시된 결과는 설명을 위한 예시이며 실제 실행 기록이 아니다.
원리 심화
04디스크립터·열린 파일 설명·inode의 공유 범위
- 세 층의 식별자의 fd 테이블은 작은 정수 번호를 에 연결한다. 에는 현재 오프셋·파일 상태 플래그가 있고 그 아래 파일 객체가 연결된다.
- 두 번의 open같은 경로를 두 번 open하면 보통 서로 다른 을 얻는다. 같은 파일의 내용은 공유하지만 두 오프셋은 독립적으로 움직인다.
- dup와 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의 읽기fd 4에서 2바이트를 읽으면 CD를 얻고 오프셋은 4가 된다. 새 번호라는 이유로 처음부터 읽는 것은 아니다.
- 별도 open 비교같은 경로를 새로 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) | 4 | 2 |
검증·예외
06경로 오류와 열린 파일 오류를 분리해 읽기
- ENOENT구성 요소가 없거나 의 대상이 없으면 이름 탐색이 실패한다. 화면에 링크 이름이 보인다는 것만으로 최종 파일의 존재가 증명되지 않는다.
- ENOTDIRa/b/c를 열 때 b가 일반 파일이면 중간 요소를 디렉터리로 통과할 수 없다. 최종 c의 권한을 바꾸어 해결되는 문제가 아니다.
- EACCES경로의 디렉터리 탐색 권한 또는 최종 파일 접근 권한에서 거부될 수 있다. 대상 파일의 mode만 읽지 말고 경로 각 요소의 실행 주체를 대입한다.
- fd 자원 한계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·마운트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는 매번 전체 경로를 다시 해석하는 대신 이 열린 참조를 사용한다.
- 파일 위치·접근 모드열린 파일 상태에는 읽기 위치와 접근 모드 등이 관계한다.
- ·파일 이름은 , 는 파일 객체의 정보, 디스크립터는 해당 에서 사용하는 참조이므로 세 값을 동일한 식별자로 생각하면 안 된다.
- 상대 경로의 기준 선택read 전용으로 notes.txt를 여는 경우를 가정한다. /로 시작하지 않는 경로이므로 현재 작업 디렉터리에서 탐색을 시작한다.
- 디렉터리 이름 탐색경로의 중간 디렉터리와 마운트 경계를 따라 최종 이름이 가리키는 파일을 찾는다. 중간 디렉터리의 탐색 권한도 필요하다.
- 열기 권한 판정파일 유형과 요청한 읽기 모드, 접근 정책을 검사한다. 존재하지 않는 이름이나 권한 거부는 열기 실패로 반환된다.
- 열린 파일 상태 생성성공하면 열린 파일 상태에 접근 모드와 현재 위치 등을 연결하고, 가 사용할 를 반환한다.
- 디스크립터로 읽고 닫기read는 이미 열린 참조를 이용하며 읽은 만큼 파일 위치가 변한다. 사용 후 close로 해당 디스크립터의 참조를 해제한다.
설명과 함께 보는 보조 도식
처리 과정
상대 경로에서 열린 파일까지
기존 일반 파일을 읽기 모드로 여는 성공 경로다.
01프로세스작업 디렉터리 /srv와 상대 경로를 가진다.
02경로 탐색notes와 day.txt의 디렉터리 항목을 찾는다.
03파일 객체종류와 소유권 등 메타데이터를 확인한다.
04열린 파일접근 모드와 현재 위치를 가진 열린 참조다.
05디스크립터프로세스가 이후 read에 사용하는 번호다.
01
기준 선택
/로 시작하지 않으므로 현재 작업 디렉터리에서 출발한다.
- 기준
- /srv
전체 단계를 한 번에 읽기
- 01
기준 선택
/로 시작하지 않으므로 현재 작업 디렉터리에서 출발한다.
- 기준
- /srv
- 02
이름 탐색
중간 디렉터리와 마운트를 따라 최종 이름을 해석한다.
- 대상
- notes/day.txt
- 03
접근 판정
파일 유형과 요청한 읽기 권한을 검사한다.
- 요청
- read
- 04
열린 참조 생성
읽기 위치와 모드를 가진 열린 파일 상태를 연결한다.
- 초기 위치
- 파일 시작
- 05
참조로 읽기
반환된 디스크립터를 이용해 읽고 사용 후 닫는다.
- 경로 재탐색
- 일반적인 read에서는 불필요
11이름이 사라졌는데 공간이 남는 이유
- 디렉터리·du삭제된 로그에 열린 참조가 남으면 디렉터리를 합산한 du와 파일 시스템 전체 사용량인 df가 다르게 보일 수 있다.
- 단순히 두 도구 중 하나가 잘못됐다고 판단하지 않고 앞에서 구분한 와 열린 참조를 대조한다.
- 현재 작업 디렉터리경로를 조사할 때는 현재 작업 디렉터리, 각 중간 구성 요소의 유형과 권한, 링크 대상, 최종 마운트를 순서대로 대조한다.
- 경로 탐색 실패 유형존재하지 않는 구성 요소, 디렉터리가 아닌 중간 파일, 통과 권한 부족은 서로 다른 실패다.
- 파일 이름의 재사용같은 이름을 삭제하고 새 파일을 만들었다면 기존에 열린 참조와 새 경로가 다른 파일을 가리킬 수도 있다.
- 로그 교체와 열린 참조로그 교체 뒤 가 어느 파일에 쓰는지 확인해야 하는 이유가 여기에 있다.
| 구성·관찰 대상 | 확인하는 정보 | 구분할 문제 |
|---|---|---|
| 이름과 파일 객체의 연결 | 이름 변경·삭제·끊어진 링크 | |
| 유형·소유권· | 이름은 달라도 같은 파일인지 | |
| 열린 파일 참조 | 접근 모드와 읽기 위치 | 이름 삭제 후에도 남은 사용 |
| 마운트 | 경로와 파일 시스템의 연결 | 다른 장치의 파일이 보이는지 |
학습 확인
이해했는지 확인하기
- 상대 경로의 기준 디렉터리를 확인한다.
- 심볼릭 링크와 실제 대상의 상태를 구분한다.
- 파일 경로와 실제 저장 장치의 연결을 확인한다.