리눅스 기초 · 파일과 디렉터리 · 이론
이름, inode, 그리고 링크
한 줄 요약
리눅스에서 파일 이름은 실체가 아니라 실체를 가리키는 하나의 참조일 뿐이다. 이 문장 하나에서 오늘 배울 것 전부가 따라 나온다.
왜 이게 필요했나
운영 중인 서버에서 로그가 디스크를 다 먹었다. rm으로 큰 로그 파일을 지웠는데 df의 여유 공간이 1바이트도 늘지 않는다. 다들 한 번씩 겪고, 한 번씩 당황한다.
여기서 "rm 이 안 먹혔다"고 결론 내리면 그날 밤이 길어진다. 실제로 일어난 일은 이렇다. rm은 파일을 지우는 명령이 아니라 디렉터리에서 이름을 하나 떼어내는(unlink) 명령이다. 데이터 블록이 반환되려면 두 조건이 동시에 성립해야 한다.
1. 그 실체를 가리키는 이름이 하나도 남아 있지 않고
2. 그 실체를 열어 둔 프로세스도 하나도 없을 때
로그를 쓰던 애플리케이션이 아직 그 파일을 열고 있으면 두 번째 조건이 깨진다. 이름은 사라졌지만 데이터는 살아 있고, du는 경로를 걸어다니며 세기 때문에 이름 없는 그 파일을 영원히 찾지 못한다. df 와 du 가 어긋나는 순간이 바로 이 상황의 신호다.
어떻게 동작하나
세 가지를 구분해야 한다.
| 개념 | 무엇인가 | 이름을 담고 있나 |
| --- | --- | --- |
| inode | 파일의 실체. 크기·권한·소유자·시각·링크 수·데이터 블록 위치 | 담고 있지 않다 |
| 디렉터리 엔트리 | (이름, inode 번호) 짝 | 이름은 여기에만 있다 |
| 파일 디스크립터 | 프로세스가 연 파일을 가리키는 번호 | 담고 있지 않다 |
디렉터리는 결국 이 짝의 목록일 뿐이다. 그래서 다음이 전부 자연스럽게 설명된다.
- 하드 링크: 같은 inode에 이름을 하나 더 붙인 것.
ls -li로 보면 inode 번호가 같고 링크 수가 2다. 원본을 지워도 다른 이름이 남아 있으므로 데이터는 그대로다. 단, inode 번호는 파일시스템 안에서만 유일하므로 하드 링크는 같은 파일시스템 안에서만 가능하고 디렉터리에는 걸 수 없다. - 심볼릭 링크: 경로 문자열을 담고 있는 별개의 파일. inode가 다르고, 원본이 사라지면 깨진다. 대신 파일시스템을 넘나들 수 있다.
- 이름 바꾸기(mv): 같은 파일시스템 안에서는 데이터를 전혀 옮기지 않는다. 디렉터리 엔트리만 고쳐 쓴다. 그래서 1GB 파일을 옮겨도 즉시 끝난다.
현장에서 만나는 모습
첫째, 지운 로그 복구. 프로세스가 아직 열고 있다면 /proc/PID/fd 아래에 그 파일로 가는 심링크가 살아 있다. ls -l /proc/1234/fd | grep deleted로 찾아 cp /proc/1234/fd/7 /backup/recovered.log를 하면 내용을 통째로 건질 수 있다. 여기서 가장 중요한 건 순서다 - 프로세스를 재기동하는 순간 복구 기회도 함께 사라진다. 이 상황에서 첫 행동은 재기동이 아니라 복사여야 한다.
둘째, 세대 백업의 용량 폭발. rsync의 --link-dest는 바뀌지 않은 파일을 하드 링크로 연결해 세대 백업을 싸게 만든다. 그런데 그 백업 디렉터리를 하드 링크를 모르는 도구로 복사하면 용량이 몇 배로 튄다. rsync -aH처럼 링크를 보존하는 옵션을 명시해야 하고, -H는 -a에 포함되지 않는다.
셋째, inode 고갈. ext4는 포맷 시점에 inode 개수를 고정하고 나중에 늘릴 수 없다. 세션 파일이나 메일 큐가 작은 파일 수백만 개를 만들면 블록은 남아도 inode가 먼저 떨어져 No space left on device가 난다. 그래서 용량 알람은 df -h와 df -i 둘 다 걸어야 한다.
다음 실습에서 할 것
/root/work 아래에 작업 공간을 만들고, 하드 링크와 심볼릭 링크를 직접 걸어 inode 번호와 링크 수가 어떻게 달라지는지 눈으로 확인한다. 마지막에는 로그 디렉터리를 통째로 아카이브로 묶어 본다.