追踪容量与 inode
目标
亲手制造 df 和 du 给出不同答案的情形,并确认 inode 消耗、稀疏文件和硬链接对容量计算的影响。
为什么重要
df 是向文件系统询问,du 则是遍历目录来统计。所以已经失去名称的文件(已经 rm,但进程仍然打开着它)会被 df 统计到,却不会被 du 统计到。两个值对不上,本身就是有力的线索。而且要恢复这个文件,必须在重启进程之前,从 /proc/PID/fd 中把它复制出来——一重启,机会就没了。即使容量告急,第一个动作也是复制。
步骤
- 创建目录
/root/usage,把df -h的输出保存到/root/usage/df-block.txt,把df -i的输出保存到/root/usage/df-inode.txt。 - 把
/root之下深度为 1 以内的目录大小按从大到小排序,保存到/root/usage/du-top.txt。 - 在
/root/usage/many之下创建 3000 个空文件,并把该目录的文件数量以一行数字写入/root/usage/filecount.txt。 - 把
/root/usage/sparse.img创建为 100MiB 的稀疏文件,并把逻辑大小和实际占用各写一行(以字节为单位,逻辑在前),保存到/root/usage/sparse.txt。两个值必须不同。 - 创建一个已删除但仍被打开的文件。向
/root/usage/ghost.log写入至少 5MiB,在后台启动一个持续打开该文件的进程,然后删除文件名。再把该进程的/proc/<PID>/fd列表中可以看到(deleted)的那一行保存到/root/usage/deleted.txt。 - 向
/root/usage/orig.dat写入 1MiB,并创建硬链接/root/usage/link.dat。把两个文件的 inode 编号和链接数,以<inode> <링크수>(占位符依次为 inode 编号、链接数)的格式各写一行,保存到/root/usage/hardlink.txt。两行的 inode 必须相同。 - 把
/root/usage之下大小在 1MiB 以上的文件按大小降序,以<바이트> <경로>(占位符依次为字节数、路径)的格式保存到/root/usage/big.txt。 - 把
/root/usage/report.txt写成下面这 4 行。FILES=<3번 값>/SPARSE_DIFF=yes/GHOST_PID=<5번에서 파일을 열고 있는 프로세스의 PID>/SAME_INODE=yes(第 1 项和第 3 项的占位符依次为第 3 步的值、第 5 步中打开该文件的进程的 PID)
参考
- 创建已删除但仍被打开的文件:用
tail -f /root/usage/ghost.log >/dev/null 2>&1 & echo $!得到 PID,然后执行rm /root/usage/ghost.log。 - 稀疏文件:
truncate -s 100M,实际占用用du -B1,逻辑大小用stat -c %s。 - 用
stat -c '%i %h' <파일>(占位符为文件名)可以同时查看 inode 编号和链接数。 - 常见错误 1:在第 5 步删除文件之后连进程也杀掉,就观察不到
(deleted)。 - 常见错误 2:在第 3 步,文件多的时候
ls | wc -l会很慢。find ... -type f | wc -l更好。
块与 inode 的使用量
创建目录 /root/usage,把 df -h 的输出保存到 /root/usage/df-block.txt,把 df -i 的输出保存到 /root/usage/df-inode.txt。
df 有便于人阅读的单位选项,也有查看 inode 的选项,二者是分开的。
找出大目录
把 /root 之下深度为 1 以内的目录大小按从大到小排序,保存到 /root/usage/du-top.txt。
限制深度,就可以从上往下逐步缩小范围。也请同时使用不跨入其他文件系统的选项。
大量创建小文件
在 /root/usage/many 之下创建 3000 个空文件,并把该目录的文件数量以一行数字写入 /root/usage/filecount.txt。
统计文件数量与查看 inode 使用量是两回事。请对比创建前后。
稀疏文件的两种大小
把 /root/usage/sparse.img 创建为 100MiB 的稀疏文件,并把逻辑大小和实际占用各写一行(以字节为单位,逻辑在前),保存到 /root/usage/sparse.txt。两个值必须不同。
du 默认统计的是实际占用。要看逻辑大小,需要另外的选项。
重现已删除但仍被打开的文件
创建一个已删除但仍被打开的文件。向 /root/usage/ghost.log 写入至少 5MiB,在后台启动一个持续打开该文件的进程,然后删除文件名。再把该进程的 /proc/<PID>/fd 列表中可以看到 (deleted) 的那一行保存到 /root/usage/deleted.txt。
让打开该文件的进程保持在后台运行,然后只删除文件名。/proc 之下的 fd 符号链接上会留下标记。
硬链接与 du
向 /root/usage/orig.dat 写入 1MiB,并创建硬链接 /root/usage/link.dat。把两个文件的 inode 编号和链接数,以 <inode> <링크수>(占位符依次为 inode 编号、链接数)的格式各写一行,保存到 /root/usage/hardlink.txt。两行的 inode 必须相同。
请确认当有多个名称指向同一个 inode 时,du 会统计几次。链接数用 stat 查看。
大文件列表
把 /root/usage 之下大小在 1MiB 以上的文件按大小降序,以 <바이트> <경로>(占位符依次为字节数、路径)的格式保存到 /root/usage/big.txt。
find 的 -size 接受单位后缀。要同时输出大小和路径,请使用 -printf。
容量调查报告
把 /root/usage/report.txt 写成下面这 4 行。
FILES=<3번 값> / SPARSE_DIFF=yes / GHOST_PID=<5번에서 파일을 열고 있는 프로세스의 PID> / SAME_INODE=yes(第 1 项和第 3 项的占位符依次为第 3 步的值、第 5 步中打开该文件的进程的 PID)
把前面步骤的结果汇总成 键=值。已删除但仍被打开的文件的大小,对 /proc 路径执行 stat 就能得到。