服务器磁盘满了,SSH 登录都报错 No space left on device。第一反应就是敲 df -h,但有时候明明显示还有空间,却还是报错------这是 inode 耗尽了。深入了解 df 命令后,发现这个看似简单的工具其实藏着不少门道。
df 的底层实现:statvfs 系统调用
df 命令的核心数据来自 statvfs() 系统调用,它返回文件系统的统计信息:
bash
#include <sys/statvfs.h>
int statvfs(const char *path, struct statvfs *buf);
struct statvfs {
unsigned long f_bsize; // 文件系统块大小
fsblkcnt_t f_blocks; // 总块数
fsblkcnt_t f_bfree; // 空闲块数
fsblkcnt_t f_bavail; // 普通用户可用块数
fsfilcnt_t f_files; // 总 inode 数
fsfilcnt_t f_ffree; // 空闲 inode 数
// ...
};
关键点在于 f_bfree 和 f_bavail 的区别:
- f_bfree:文件系统实际的空闲块数
- f_bavail:普通用户可用的块数(预留了 5% 给 root)
这就是为什么 df -h 显示 Use% 会超过 100% ------ root 用户可以继续写入,直到用完那 5% 预留空间。
常用参数详解
-h:人类可读格式 (Human-Readable Format)
bash
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 100G 85G 10G 90% /
tmpfs 7.8G 0 7.8G 0% /dev/shm
-h 会自动选择合适的单位(K/M/G/T),比默认的 1K 块易读得多。
-i:inode 使用情况
bash
$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda1 655360 655350 10 100% /
这就是 inode 耗尽的典型表现:Use% 只有 90%,但 IUse% 是 100%。大量小文件(比如日志切分、缓存文件)会消耗 inode,即使磁盘空间还充足。
-T:显示文件系统类型
bash
$ df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/sda1 ext4 104755200 85000000 10000000 90% /
/dev/sdb1 xfs 209715200 50000000 159715200 20% /data
不同文件系统的特点:
| 文件系统 | 最大文件大小 | 最大卷大小 | 特点 |
|---|---|---|---|
| ext4 | 16TB | 1EB | 成熟稳定,Linux 默认 |
| xfs | 8EB | 8EB | 高并发,适合大文件 |
| btrfs | 16EB | 16EB | 快照、压缩、校验 |
| tmpfs | 内存限制 | 内存限制 | 内存文件系统,速度快 |
--total:汇总统计
bash
$ df -h --total
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 100G 85G 10G 90% /
/dev/sdb1 200G 50G 150G 20% /data
total 300G 135G 160G 45% -
适合快速统计总容量。
实战场景
1. 磁盘空间预警脚本
bash
#!/bin/bash
# 监控磁盘使用率,超过阈值发邮件告警
THRESHOLD=80
df -H | grep -vE '^Filesystem|tmpfs|cdrom' | while read line; do
usage=$(echo "$line" | awk '{print $5}' | sed 's/%//')
mount=$(echo "$line" | awk '{print $NF}')
if [ "$usage" -gt "$THRESHOLD" ]; then
echo "警告: $mount 使用率 ${usage}%" | mail -s "磁盘空间告警" admin@example.com
fi
done
2. inode 耗尽排查
bash
# 查找哪个目录 inode 最多
for dir in /*; do
if [ -d "$dir" ]; then
count=$(find "$dir" -xdev | wc -l)
echo "$count $dir"
fi
done | sort -rn | head -10
通常是 /var/spool/postfix/maildrop 或 /tmp 下堆积了大量小文件。
3. 结合 du 定位大文件目录
bash
# 先用 df 看哪个分区满了
df -h
# 再用 du 定位大目录
du -h --max-depth=1 / | sort -hr | head -10
4. 查看挂载点真实使用情况
bash
# -x 排除指定文件系统类型
df -h -x tmpfs -x devtmpfs
# -t 只显示指定类型
df -h -t ext4 -t xfs
性能考量
df 命令本身的性能通常不是问题,但在特殊场景下需要注意:
NFS 挂载超时
bash
# NFS 服务器宕机时,df 会卡住
# 解决:使用 timeout 或后台运行
timeout 5 df -h /mnt/nfs
大量挂载点
bash
# 查看所有挂载点(包括 docker、snap 等)
$ df -h | wc -l
127
系统运行久了会有很多挂载点,df 会逐个查询,可能变慢。
常见陷阱
1. 删除文件后空间未释放
bash
$ df -h /data
/dev/sdb1 200G 180G 20G 90% /data
$ rm -f /data/large_file
$ df -h /data
/dev/sdb1 200G 180G 20G 90% /data # 空间没变!
原因:文件被进程占用,删除只是标记为待删除。
bash
# 查找占用已删除文件的进程
$ lsof +L1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 www-data 3w REG 8,17 50G 123 /data/large_file (deleted)
# 重启 nginx 释放空间
$ systemctl reload nginx
2. 预留空间误判
bash
$ df -h /
/dev/sda1 100G 95G 0G 95% /
# 普通用户写入失败
$ touch /test
touch: cannot touch '/test': No space left on device
# root 用户还能写
# sudo touch /test # 成功
ext4 默认预留 5% 给 root,对于大磁盘(比如 10TB)可以降低:
bash
# 降到 1%
tune2fs -m 1 /dev/sda1
3. tmpfs 内存消耗
bash
$ df -h /dev/shm
tmpfs 7.8G 0 7.8G 0% /dev/shm
tmpfs 占用的是内存,写入大文件会导致内存不足。生产环境建议限制大小:
bash
mount -o remount,size=2G /dev/shm
Web 实现:浏览器端磁盘监控
虽然浏览器无法直接访问本地磁盘,但可以通过 Web API 实现"虚拟磁盘"概念:
bash
// 使用 Storage API 监控 localStorage 配额
async function checkStorageQuota() {
const estimate = await navigator.storage.estimate()
const usedMB = (estimate.usage || 0) / 1024 / 1024
const quotaMB = (estimate.quota || 0) / 1024 / 1024
const percent = ((estimate.usage || 0) / (estimate.quota || 1)) * 100
return {
used: usedMB.toFixed(2) + ' MB',
quota: quotaMB.toFixed(2) + ' MB',
percent: percent.toFixed(1) + '%',
warning: percent > 80
}
}
// 模拟 df -h 输出
function formatDiskInfo(info: DiskInfo): string {
const pad = (str: string, len: number) => str.padEnd(len)
return [
pad('Filesystem', 20),
pad('Size', 10),
pad('Used', 10),
pad('Avail', 10),
pad('Use%', 6),
'Mounted on'
].join(' ') + '\n' +
[
pad(info.fs, 20),
pad(info.total, 10),
pad(info.used, 10),
pad(info.avail, 10),
pad(info.percent, 6),
info.mount
].join(' ')
}
深入了解 df 命令后,发现它不只是一个简单的磁盘查看工具。从 inode 耗尽到预留空间,从 NFS 超时到文件占用,每个场景都有对应的排查思路。下次遇到磁盘问题,别只看 Use%,记得 -i 也看看。