文件系统使用量

磁盘空间监控 对于 应对存储短缺故障 以及 前瞻性容量规划 意义重大。

注解

磁盘空间监控这个说法其实不太准确: 磁盘 只提供底层块存储,因而只有总容量的概念; 至于当前 使用量剩余可用量 等都归 文件系统 管理。 因此,正如标题所示,本文采用 文件系统使用量 这个说法。

除了 存储空间 资源,管理员还需要关注 文件节点 资源。 对于大部分文件系统, inode 节点数是固定的,也就是说能存储的文件数是固定的。 因此,在文件节点耗尽的情况下,就算存储空间还有剩余也无济于事。

指标

文件系统使用量 可以通过 statvfs 系统调用 获取,指标涵盖 存储空间 以及 文件节点

文件系统使用量
指标名 单位 含义
total_bytes 字节 总存储空间
free_bytes 字节 空闲存储空间
available_bytes 字节 可用存储空间(非特权用户)
used_bytes 字节 已用存储空间
used_bytes_percent 百分比 已用存储空间占比
total_files 字节 总文件节点数( inode )
free_files 字节 空闲文件节点数
available_files 字节 可用文件节点数(非特权用户)
used_files 字节 已用文件节点数
used_files_percent 百分比 已用文件节点数占比

total_bytes

total_bytes 表示 总存储空间 ,单位为 字节 ,由 statvfs 相关字段计算而来:

\[total\_bytes = statvfs.f\_frsize * statvfs.f\_blocks\]

statvfs 结构体定义如下:

struct statvfs {
    unsigned long  f_bsize;    /* Filesystem block size */
    unsigned long  f_frsize;   /* Fragment size */
    fsblkcnt_t     f_blocks;   /* Size of fs in f_frsize units */
    fsblkcnt_t     f_bfree;    /* Number of free blocks */
    fsblkcnt_t     f_bavail;   /* Number of free blocks for
                                  unprivileged users */
    fsfilcnt_t     f_files;    /* Number of inodes */
    fsfilcnt_t     f_ffree;    /* Number of free inodes */
    fsfilcnt_t     f_favail;   /* Number of free inodes for
                                  unprivileged users */
    unsigned long  f_fsid;     /* Filesystem ID */
    unsigned long  f_flag;     /* Mount flags */
    unsigned long  f_namemax;  /* Maximum filename length */
};

free_bytes

free_bytes 表示 空闲存储空间 ,单位为 字节 ,同样由 statvfs 相关字段计算而来:

\[free\_bytes = statvfs.f\_frsize * statvfs.f\_bfree\]

available_bytes

available_bytes 表示 可用存储空间 ,单位为 字节 ,同样由 statvfs 相关字段计算而来:

\[available\_bytes = statvfs.f\_frsize * statvfs.f\_bavail\]

注解

文件系统通常为 root 用户预留一部分空间,其他非特权用户不能使用。 对 root 用户,可用空间是 free_bytes ; 对非特权用户,可用空间是 available_bytes 。 一般而言, free_bytesavailable_bytes 大:

\[free\_bytes > available\_bytes\]

used_bytes

used_bytes 表示 已用存储空间 ,即总存储空间减去空闲存储空间:

\[used\_bytes = total\_bytes - free\_bytes\]

used_bytes_percent

used_bytes_percent 表示 可用存储空间百分比 ,计算公式如下:

\[used\_bytes\_percent = \frac{used\_bytes}{used\_bytes + available\_bytes} \times 100\%\]

读者可能觉得奇怪,为什么不是除以 total_bytes 呢? ——因为对于非特权用户,可用存储空间是 available_bytes 而不是 free_bytes 。 对非特权用户来说,总存储空间是:

\[total\_bytes\_for\_nonroot = used\_bytes + available\_bytes\]

因此,已用存储空间百分比是针对非特权用户计算的, df 命令也是采用这个口径。

文件节点系列

文件节点 系列指标与 存储空间 系列类似,不再赘述。计算公式列举如下:

\[total\_files = statvfs.f\_files\]
\[free\_files = statvfs.f\_ffree\]
\[available\_files = statvfs.f\_favail\]
\[used\_files = total\_files - free\_files\]
\[used\_files\_percent = \frac{used\_files}{used\_files + available\_files} \times 100\%\]

采集

借助 statvfs 系统调用 ,采集某个文件系统使用量统计毫无难度。

可问题是,如何获取所有已挂载的文件系统呢? ——答案是 proc 伪文件系统,内核将所有文件系统挂载点暴露在 /proc/mounts 文件中。

这是一个简单的示例程序,依次展示每个文件系统使用量统计:

fs_usage.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import os

from tabulate import (
    tabulate,
)

TABLE_HEADER = (
    'mount_point',

    'total_bytes',
    'used_bytes',
    'used_bytes_percent',

    'total_files',
    'used_files',
    'used_files_percent',
)

def get_mount_points():
    with open('/proc/mounts') as f:
        return [
            line.strip().split()
            for line in f
        ]


def get_fs_usage():
    devices = set()
    table_data = []
    for device, mount_point, _, _, _, _ in get_mount_points():
        # skip non-device
        if not device.startswith('/dev'):
            continue

        if device in devices:
            continue

        devices.add(device)

        # call statvfs to fetch file system statistics
        statvfs = os.statvfs(mount_point)

        f_frsize = statvfs.f_frsize

        # calculate space
        total_bytes = statvfs.f_blocks * f_frsize
        free_bytes = statvfs.f_bfree * f_frsize
        available_bytes = statvfs.f_bavail * f_frsize

        used_bytes = total_bytes - free_bytes
        used_bytes_percent = 100. * used_bytes / (used_bytes + available_bytes)

        # calculate files
        total_files = statvfs.f_files
        free_files = statvfs.f_ffree
        available_files = statvfs.f_favail

        used_files = total_files - free_files
        used_files_percent = 100. * used_files / (used_files + available_files)

        table_data.append((
            mount_point,
            total_bytes,
            used_bytes,
            used_bytes_percent,
            total_files,
            used_files,
            used_files_percent,
        ))

    print(tabulate(table_data, TABLE_HEADER, floatfmt='6.2f'))
    print()

if __name__ == '__main__':
    get_fs_usage()

get_mount_points 函数读取 /proc/mounts 文件,从中切分出挂载点并返回。

get_fs_usage 函数遍历每个文件系统挂载点,调用 statvfs 获取并计算使用量统计:

  1. 30 行,循环遍历每个文件系统挂载点;
  2. 31 行,跳过一些非设备文件系统,如 procfs 伪文件系统;
  3. 35-38 行配合,跳过重复挂载点;
  4. 41 行,调用 statvfs 系统调用,获取文件系统统计值;
  5. 46-59 行,根据上节相关公式计算所有指标;

下一步

订阅更新,获取更多学习资料,请关注我们的 微信公众号

../../../_images/wechat-mp-qrcode.png

小菜学编程