文件系统使用量

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

注解

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

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

指标

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

文件系统使用量
指标名单位含义
totalbytes字节总存储空间
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 结构体定义如下:

  1. struct statvfs {
  2. unsigned long f_bsize; /* Filesystem block size */
  3. unsigned long f_frsize; /* Fragment size */
  4. fsblkcnt_t f_blocks; /* Size of fs in f_frsize units */
  5. fsblkcnt_t f_bfree; /* Number of free blocks */
  6. fsblkcnt_t f_bavail; /* Number of free blocks for
  7. unprivileged users */
  8. fsfilcnt_t f_files; /* Number of inodes */
  9. fsfilcnt_t f_ffree; /* Number of free inodes */
  10. fsfilcnt_t f_favail; /* Number of free inodes for
  11. unprivileged users */
  12. unsigned long f_fsid; /* Filesystem ID */
  13. unsigned long f_flag; /* Mount flags */
  14. unsigned long f_namemax; /* Maximum filename length */
  15. };

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

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

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

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

下一步

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

../../../_images/wechat-mp-qrcode.png小菜学编程

参考文献