Linux df 命令深度解析:从磁盘空间监控到 inode 耗尽排查

服务器磁盘满了,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_bfreef_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 也看看。

Linux du 命令参考

相关工具:内存单位转换器 | 端口检测器

相关推荐
3301_2 小时前
Debian13 ThinkPad T490安装指纹解锁
linux
Mortalbreeze2 小时前
深度理解进程----进程状态
linux·运维·服务器
艾莉丝努力练剑2 小时前
【Linux网络】Linux 网络编程入门:TCP Socket 编程(下)
linux·运维·服务器·网络·c++·tcp/ip
宵时待雨2 小时前
linux笔记归纳4:进程概念
linux·运维·服务器·c++·笔记
零K沁雪2 小时前
OpenV_X_N 2.5.x 配置文件选项详解
linux
原来是猿2 小时前
TCP Echo Server 深度解析:从单进程到线程池的演进之路(中)
linux·服务器·数据库
fTiN CAPA2 小时前
Linux系统离线部署MySQL详细教程(带每步骤图文教程)
linux·mysql·adb
xlq223223 小时前
52.TcpSocket
linux·服务器·网络
剑神一笑3 小时前
Linux xargs 命令深度解析:从管道到命令构建的桥梁
linux·运维·chrome