빌드노트
ls에는 8MiB인데 du는 0인 파일: sparse 크기 읽기
같은 8MiB 파일을 tmpfs와 WSL 9p에서 만들고 stat·du·du --apparent-size를 비교했다. 논리 길이와 할당 블록의 차이, du -b의 의미, sparse 절약이 보이지 않을 때 확인할 파일시스템 조건을 설명한다.
ls -l의 크기 열에는 8MiB가 있는데 du는 0을 표시할 수 있다. 파일의 논리 길이와 파일시스템이 보고한 할당량은 다른 값이다. sparse 파일은 길이 안에 데이터 블록을 할당하지 않은 구간인 hole을 둘 수 있다. 이 경우 파일 끝까지의 길이가 커도 할당량은 작다. 숫자가 다르다는 이유만으로 파일 손상이나 명령 오류로 판단할 필요는 없다.
파일의 길이가 궁금하면 stat의 크기나 du --apparent-size를, 파일시스템이 보고하는 할당량이 궁금하면 일반 du를 본다. 비교할 때는 둘 다 바이트로 표시해야 단위 차이를 크기 차이로 오해하지 않는다. GNU du의 크기 기준 (새 탭에서 열림)
같은 파일을 두 기준으로 조회한다
Linux와 GNU coreutils를 쓰는 셸에서 다음 세 명령으로 논리 크기와 할당량을 비교한다. 아래 이름은 실험 파일 이름이다. 자기 파일을 조사할 때는 sparse.bin data.bin을 그 파일의 경로로 바꾸면 된다. 파일 하나만 지정해도 되며, 공백이 있는 경로는 따옴표로 감싼다. 조회 대상은 일반 파일이고, 명령은 파일 내용을 바꾸지 않는다.
stat -c '%n size=%s blocks=%b unit=%B' -- sparse.bin data.bin
du -B1 -- sparse.bin data.bin
du --apparent-size -B1 -- sparse.bin data.bin
WSL2의 Ubuntu 24.04.4, GNU coreutils 9.4에서 새 tmpfs 폴더에 두 파일을 만들어 실행했다. 출력은 다음과 같았다. 처음 두 줄은 stat, 다음 두 줄은 du -B1, 마지막 두 줄은 du --apparent-size -B1의 결과다.
sparse.bin size=8388608 blocks=0 unit=512
data.bin size=8388608 blocks=16384 unit=512
0 sparse.bin
8388608 data.bin
8388608 sparse.bin
8388608 data.bin
size=%s는 파일의 논리 크기를 바이트로 보여 준다. 두 파일 모두 8,388,608바이트, 즉 8MiB다. blocks=%b는 할당 블록 수이고 unit=%B는 그 블록 하나의 바이트 수다. 이 출력에서 data.bin의 보고된 할당량은 16384 × 512 = 8388608바이트다. sparse.bin은 블록 수가 0이었다. 여기서 512는 이 통계의 단위이므로 파일시스템의 블록 크기를 짐작해 4096을 곱하면 안 된다. GNU stat의 서식 항목 (새 탭에서 열림)
du -B1도 각각 0과 8,388,608을 표시했다. 이 0은 파일의 데이터 블록에 대한 보고값이며, 파일 객체와 디렉터리 등의 메타데이터 비용까지 0이라는 뜻은 아니다. -B1은 표시 단위를 1바이트로 바꾸며, 측정 대상을 논리 길이로 바꾸지는 않는다. 반면 --apparent-size를 넣으면 두 파일 모두 논리 길이인 8,388,608이 된다.
여기서 헷갈리기 쉬운 옵션이 du -b다. GNU에서는 --apparent-size --block-size=1과 같은 뜻이다. 실제로 같은 두 파일에 실행했을 때 모두 8,388,608이 나왔다. 할당량을 바이트로 확인하려고 -b를 고르면 처음부터 다른 값을 묻게 된다. GNU du의 -b 설명 (새 탭에서 열림)
길이만 늘린 파일과 데이터를 쓴 파일
작게 재현하려면 조사할 파일시스템에 새 빈 폴더를 준비한다. GNU truncate와 dd가 필요하다. 같은 이름의 기존 파일이 있으면 크기나 내용을 바꾸므로 반드시 새 폴더에서 실행한다. 다음 두 명령은 각각 8MiB 파일을 만들며, 오류 없이 끝나면 별도 출력이 없다. 오류가 나면 생성에 성공한 것으로 간주하지 말고 그 내용을 먼저 확인한다.
truncate -s 8M sparse.bin
dd if=/dev/urandom of=data.bin bs=1M count=8 status=none
첫 명령은 새 파일의 길이를 8MiB로 늘린다. 두 번째는 임의의 데이터 8MiB를 실제로 써서 비교 파일을 만든다. 여기서 M은 1,048,576배다. 생성 뒤 앞의 세 조회 명령을 실행하면 같은 길이에서 할당량이 달라지는지 확인할 수 있다. tmpfs 실험에서 ls -l --block-size=1 -- sparse.bin data.bin의 파일별 크기 열도 둘 다 8,388,608이었다. 이 크기 열은 할당 블록의 합계가 아니다. GNU ls의 크기 표시 (새 탭에서 열림)
hole을 읽으면 그 구간은 0 바이트 값으로 읽힌다. 파일이 없거나 읽을 수 없는 구간이라는 뜻은 아니다. 이번에는 sparse 파일 전체 8MiB를 읽어 모두 0인지 별도로 확인했다. 그러나 0으로 읽힌다는 사실만으로 저장 공간이 절약됐다고 판단할 수는 없다. 할당량 조회도 함께 봐야 한다. GNU truncate의 확장 동작 (새 탭에서 열림)
두 크기가 같으면 파일이 놓인 곳을 확인한다
같은 명령을 WSL의 Windows 드라이브 마운트에서도 실행했다. 그곳에서는 두 파일 모두 size=8388608 blocks=16384 unit=512였고, 두 종류의 du도 모두 8,388,608을 표시했다. 길이 확장은 성공했고 sparse 용도로 만든 파일은 모두 0으로 읽혔지만, 그 마운트의 보고값에서는 할당량 절약을 관찰하지 못했다.
자기 파일이 놓인 파일시스템 종류는 다음처럼 확인한다. sparse.bin은 조사할 파일의 경로로 바꾼다.
stat -f -c 'filesystem=%T' -- sparse.bin
차이가 났던 실험은 filesystem=tmpfs, 차이가 없었던 Windows 드라이브 쪽은 filesystem=v9fs였다. 후자는 WSL의 9p 마운트였다. 이 결과만으로 하위 Windows 파일시스템 전체의 sparse 지원 여부를 단정할 수는 없다. 파일이 놓인 계층에서 어떤 할당 정보를 돌려주는지도 확인해야 한다. 옵션을 지원하지 않는 stat이나 du라면 GNU 도구인지부터 확인한다. 이 글의 명령은 BSD나 BusyBox 도구에서 실행한 결과가 아니다.
tmpfs는 가상 메모리에 파일을 두는 파일시스템이다. 따라서 여기서 얻은 0은 SSD의 전체 점유량을 측정한 값이 아니다. Linux 커널의 tmpfs 설명 (새 탭에서 열림) 일반 디스크 파일시스템에서도 du는 운영체제가 제공하는 정보에 의존한다. 압축이나 공유 블록이 있는 환경에서는 저장장치가 실제로 차지한 공간과 다를 수 있다. GNU du의 측정 한계 (새 탭에서 열림)
직접 확인한 범위는 같은 Linux 커널에서 만든 8MiB 일반 파일 두 개와 tmpfs·9p의 보고값이다. ext4·XFS·압축·공유 블록, 복사 뒤의 할당량은 실험하지 않았다. 파일 길이를 확인하는 일에는 stat의 %s나 du --apparent-size -B1을 쓰고, 해당 파일시스템의 할당량을 조사하는 일에는 du -B1과 %b × %B를 맞춰 본다. 삭제된 파일을 누가 열고 있어서 공간이 남는 문제는 이 비교와 별도로 조사해야 한다.