【Linux 基础】df -h 的输出信息解读

【Linux 基础】df -h 的输出信息解读

一、背景与问题

在Linux系统管理中,df -h 是最基础也是最常用的磁盘空间监控工具之一。它的输出结果通常包含以下信息:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       20G   10G  10G  50% /
tmpfs          1024M   0  1024M   0% /dev/shm
/dev/sdb1       50G   20G   30G  40% /data

其中 Use% 字段表示磁盘使用百分比,是系统管理员判断是否需要扩容或清理的核心指标。然而,许多开发人员和系统管理员往往仅关注表面数据,忽略了其背后的计算原理和潜在陷阱。

例如:当 Use% 显示为 50% 时,是否意味着磁盘还有50%的可用空间?如果实际可用空间比计算值小,会导致误判。本文将深入分析 df -h 的工作原理,探讨其计算逻辑、常见陷阱以及实际应用中的最佳实践。


二、基本原理

df -h 的核心是调用 statvfs() 系统调用,通过 /proc/mounts 获取文件系统信息,然后计算磁盘使用率。其核心计算逻辑如下:

  1. 磁盘总空间:statvfs().f_blocks * statvfs().f_bsize
  2. 已用空间:statvfs().f_blocks - statvfs().f_bfree 的差值乘以 f_bsize
  3. 可用空间:statvfs().f_bfree * f_bsize
  4. 使用百分比:100 * (statvfs().f_blocks - statvfs().f_bfree) / statvfs().f_blocks
注意:f_bsize 是文件系统块大小,不同文件系统可能不同(例如 ext4 默认 4096 字节,xfs 可能更大)。

三、环境准备

确保系统支持 statvfs() 系统调用(Linux 2.2+ 都支持)。本文使用以下环境:

  • 操作系统:Ubuntu 22.04
  • 编译器:g++ 12.3
  • 工具链:make, gcc, libutil-dev

四、核心实现

示例 1:用 C 语言实现 df -h 的核心逻辑

#include <stdio.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <string.h>

void print_df_info(const char *path) {
    struct statvfs fs;
    if (statvfs(path, &fs) != 0) {
        perror("statvfs");
        return;
    }

    // 计算总空间
    long long total = (long long)fs.f_blocks * fs.f_bsize;
    // 计算已用空间
    long long used = (long long)(fs.f_blocks - fs.f_bfree) * fs.f_bsize;
    // 计算可用空间
    long long avail = (long long)fs.f_bfree * fs.f_bsize;
    // 计算使用百分比
    double use_percent = (double)(used * 100) / total;

    printf("Filesystem\tSize\tUsed\tAvail\tUse%%\tMounted on\n");
    printf("%s\t%lld KB\t%lld KB\t%lld KB\t%.2f%%\t%s\n",
           path,
           total / 1024,
           used / 1024,
           avail / 1024,
           use_percent,
           path);
}

int main() {
    print_df_info("/");
    print_df_info("/tmp");
    return 0;
}

关键代码解释:

  1. statvfs() 获取文件系统信息,f_bsize 是文件系统块大小
  2. 使用 long long 避免整数溢出(64位系统)
  3. 单位转换为 KB(1024 字节)
  4. 使用 double 计算百分比时需注意浮点精度

编译运行:

gcc -o df_demo df_demo.c
sudo ./df_demo

示例 2:用 Python 调用 df -h 并解析输出

import subprocess
import re

def parse_df_output():
    result = subprocess.check_output(['df', '-h'], text=True)
    lines = result.split('\n')
    for line in lines[1:]:  # 跳过表头
        if not line.strip():
            continue
        parts = re.split(r'[\s+]+', line)
        filesystem, size, used, avail, use_percent, mounted = parts
        print(f"{filesystem}: {use_percent} {mounted}")

parse_df_output()

关键代码解释:

  1. 使用 subprocess 调用系统命令
  2. 正则表达式分割字段(注意多个空格)
  3. 处理多行输出和空行

潜在问题:

  • df -h 输出的 Avail 字段可能包含 0(如 /proc 文件系统)
  • 不同文件系统可能有不同的 f_bsize 值

示例 3:用 Shell 脚本监控磁盘使用率

#!/bin/bash
THRESHOLD=80
while true; do
    df -h | grep -v "Filesystem" | awk '{print $5}' | grep -Eo "[0-9]+%" | sort -n | tail -n1
    if [ $? -eq 0 ]; then
        usage=$(df -h | grep -v "Filesystem" | awk '{print $5}' | grep -Eo "[0-9]+%" | sort -n | tail -n1)
        if [ $usage -gt $THRESHOLD ]; then
            echo "Warning: Disk usage exceeds $THRESHOLD%: $usage%" | mail -s "Disk Alert" admin@example.com
        fi
    fi
    sleep 60
done

关键代码解释:

  1. 使用 grep 和 awk 提取 Use% 字段
  2. sort -n 排序后取最大值
  3. 超过阈值时发送邮件告警

性能优化:

  • 避免频繁调用 df,可缓存结果
  • 使用 inotify 监控 /proc/mounts 文件变化

五、完整案例

案例:基于 df 的磁盘监控系统

需求:实时监控所有挂载点的磁盘使用率,当超过阈值时触发告警。

实现步骤:

  1. 使用 df -h 获取所有挂载点信息
  2. 计算每个挂载点的 Use%
  3. 判断是否超过阈值
  4. 发送告警通知

完整代码:

import subprocess
import time
import smtplib
from email.message import EmailMessage

THRESHOLD = 80
MAIL_SERVER = "smtp.example.com"
MAIL_PORT = 587
MAIL_USER = "admin@example.com"
MAIL_PASS = "password"

def get_disk_usage():
    result = subprocess.check_output(['df', '-h'], text=True)
    lines = result.split('\n')
    usage = {}
    for line in lines[1:]:  # 跳过表头
        if not line.strip():
            continue
        parts = re.split(r'[\s+]+', line)
        if len(parts) >= 6:
            filesystem = parts[0]
            use_percent = float(parts[5].rstrip('%'))
            usage[filesystem] = use_percent
    return usage

def send_alert(email, subject, message):
    msg = EmailMessage()
    msg.set_content(message)
    msg['Subject'] = subject
    msg['From'] = MAIL_USER
    msg['To'] = email

    with smtplib.SMTP(MAIL_SERVER, MAIL_PORT) as server:
        server.starttls()
        server.login(MAIL_USER, MAIL_PASS)
        server.send_message(msg)

def main():
    while True:
        usage = get_disk_usage()
        for fs, percent in usage.items():
            if percent > THRESHOLD:
                message = f"Disk usage on {fs} is {percent}% (Threshold: {THRESHOLD}%)"
                send_alert("admin@example.com", "Disk Alert", message)
        time.sleep(60)

if __name__ == "__main__":
    main()

关键点:

  • 使用 re.split 处理 df -h 的输出格式
  • 邮件告警机制需配置 SMTP 服务器
  • 使用 time.sleep 控制监控频率

六、源码解析

以 df -h 的核心逻辑为例:

#include <sys/statvfs.h>
#include <stdio.h>

int main() {
    struct statvfs fs;
    if (statvfs("/", &fs) != 0) {
        perror("statvfs");
        return 1;
    }

    long long total = (long long)fs.f_blocks * fs.f_bsize;
    long long used = (long long)(fs.f_blocks - fs.f_bfree) * fs.f_bsize;
    long long avail = (long long)fs.f_bfree * fs.f_bsize;
    double use_percent = (double)(used * 100) / total;

    printf("Total: %lld KB\n", total / 1024);
    printf("Used: %lld KB\n", used / 1024);
    printf("Avail: %lld KB\n", avail / 1024);
    printf("Use%%: %.2f%%\n", use_percent);
    return 0;
}

关键点解析:

  1. statvfs() 是 Linux 提供的系统调用,用于获取文件系统信息
  2. f_bsize 是文件系统块大小,不同文件系统可能不同
  3. 使用 long long 避免溢出(64位系统)
  4. 单位转换为 KB(1024 字节)

七、进阶使用

1. 多文件系统类型支持

不同文件系统(如 ext4、xfs)对 df 的计算方式不同:

  • ext4:f_bsize 是 4096 字节
  • xfs:f_bsize 可能是 4096 或更大
  • tmpfs:f_bsize 是 1024 字节

解决方案:

if (strcmp(fs.f_fstypename, "ext4") == 0) {
    // 处理 ext4 特殊情况
}

2. 跨平台兼容性

Windows 不支持 statvfs(),需要使用 GetDiskFreeSpaceEx():

#include <windows.h>
#include <iostream>

void get_disk_usage(const char* path) {
    ULARGE_INTEGER total, free;
    if (GetDiskFreeSpaceExA(path, &free, &total, NULL)) {
        std::cout << "Total: " << total.QuadPart / 1024 << " KB" << std::endl;
        std::cout << "Free: " << free.QuadPart / 1024 << " KB" << std::endl;
    }
}

3. 高性能监控

对于需要高频监控的场景(如云服务器),建议:

  • 使用 inotify 监控 /proc/mounts 变化
  • 使用 C 编写的高性能工具
  • 避免频繁调用 df 命令

八、性能与工程实践

1. 性能优化

  • 避免频繁调用:df 是系统调用,频繁调用可能影响性能
  • 缓存结果:对于不频繁变化的磁盘信息,可缓存1分钟
  • 限制监控频率:如每5分钟检查一次

2. 异常处理

  • 权限问题:确保有权限读取 /proc/mounts
  • 文件系统类型:某些文件系统可能不支持 statvfs()(如 proc 文件系统)
  • 磁盘满:f_bfree 为0时需特殊处理

3. 安全风险

  • 权限控制:确保只有授权用户能获取磁盘信息
  • 防止信息泄露:避免将磁盘信息暴露给非授权用户
  • 输入验证:避免路径注入攻击

九、常见问题与踩坑

1. 错误示例:使用 df 获取错误结果

df -h /dev/sda1

问题:/dev/sda1 是设备文件,不是挂载点。df 会返回 No such file or directory 错误。

解决方法:使用实际挂载点(如 /、/home)

2. 错误示例:忽略 Avail 字段

df -h | grep -Eo "[0-9]+%" | sort -n | tail -n1

问题:Avail 字段也可能包含百分比值,可能导致误判

解决方法:精确匹配 Use% 字段

3. 错误示例:未考虑文件系统类型

if (fs.f_bsize < 4096) {
    // 错误处理
}

问题:某些文件系统(如 tmpfs)的 f_bsize 可能小于 4096

解决方法:检查 f_fstypename 字段


十、最佳实践

1. 推荐使用场景

  • 系统监控:实时监控磁盘使用情况
  • 容量规划:判断是否需要扩容
  • 告警系统:设置阈值触发告警
  • 容器管理:监控容器磁盘使用情况

2. 不推荐使用场景

  • 需要实时数据的场景:df 是系统调用,可能有延迟
  • 高频监控场景:建议使用 inotify 或 C 编写高性能工具
  • 需要精确计算的场景:df 可能因文件系统特性导致误差

3. 推荐方案

  • 使用 C 编写的高性能工具
  • 结合 inotify 实现动态监控
  • 使用 Prometheus + Node Exporter 实现监控可视化
  • 使用 Grafana 实现数据展示

十一、总结

df -h 是 Linux 系统中最重要的磁盘监控工具之一,其核心原理是调用 statvfs() 系统调用获取文件系统信息,并计算磁盘使用率。在实际应用中,需要注意:

  1. 理解计算逻辑:Use% 的计算基于 f_blocks 和 f_bfree,不同文件系统可能有差异
  2. 避免常见陷阱:如设备文件、文件系统类型差异、缓存结果等
  3. 优化性能:避免频繁调用 df,使用缓存机制
  4. 安全处理:防止权限问题和信息泄露
  5. 结合监控系统:使用 Prometheus 等工具实现可视化监控

在开发中,建议根据具体场景选择合适的实现方式。对于需要高性能的场景,推荐使用 C 编写的工具;对于日常运维,使用 df -h 和 awk 脚本即可满足需求。理解 df -h 的原理,不仅能帮助我们更准确地判断磁盘状态,还能避免因误判导致的系统故障。

最后修改于:2026年09月20日 17:58

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日