文件系统使用量

    注解

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

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

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

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

    [total_bytes = statvfs.f_frsize * statvfs.f_blocks]

    statvfs 结构体定义如下:

    free_bytes

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

    [free_bytes = statvfs.f_frsize * statvfs.f_bfree]

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

    [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 = \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]

    [available_files = statvfs.f_favail]

    [used_files = total_files - free_files]

    [used_files_percent = \frac{used_files}{used_files + available_files} \times 100\%]

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

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

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

    fs_usage.py

    1. from tabulate import (
    2. tabulate,
    3. )
    4. TABLE_HEADER = (
    5. 'mount_point',
    6. 'total_bytes',
    7. 'used_bytes',
    8. 'used_bytes_percent',
    9. 'total_files',
    10. 'used_files',
    11. 'used_files_percent',
    12. )
    13. def get_mount_points():
    14. with open('/proc/mounts') as f:
    15. line.strip().split()
    16. for line in f
    17. ]
    18. def get_fs_usage():
    19. devices = set()
    20. table_data = []
    21. for device, mount_point, _, _, _, _ in get_mount_points():
    22. # skip non-device
    23. if not device.startswith('/dev'):
    24. continue
    25. if device in devices:
    26. continue
    27. devices.add(device)
    28. # call statvfs to fetch file system statistics
    29. statvfs = os.statvfs(mount_point)
    30. f_frsize = statvfs.f_frsize
    31. # calculate space
    32. total_bytes = statvfs.f_blocks * f_frsize
    33. free_bytes = statvfs.f_bfree * f_frsize
    34. available_bytes = statvfs.f_bavail * f_frsize
    35. used_bytes = total_bytes - free_bytes
    36. # calculate files
    37. total_files = statvfs.f_files
    38. free_files = statvfs.f_ffree
    39. available_files = statvfs.f_favail
    40. used_files = total_files - free_files
    41. used_files_percent = 100. * used_files / (used_files + available_files)
    42. table_data.append((
    43. mount_point,
    44. total_bytes,
    45. used_bytes,
    46. used_bytes_percent,
    47. total_files,
    48. used_files,
    49. used_files_percent,
    50. ))
    51. print(tabulate(table_data, TABLE_HEADER, floatfmt='6.2f'))
    52. print()
    53. if __name__ == '__main__':
    54. get_fs_usage()

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

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

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

    订阅更新,获取更多学习资料,请关注我们的 :