文件系统使用量¶
磁盘空间监控 对于 应对存储短缺故障 以及 前瞻性容量规划 意义重大。
注解
磁盘空间监控这个说法其实不太准确: 磁盘 只提供底层块存储,因而只有总容量的概念; 至于当前 使用量 、 剩余可用量 等都归 文件系统 管理。 因此,正如标题所示,本文采用 文件系统使用量 这个说法。
除了 存储空间 资源,管理员还需要关注 文件节点 资源。 对于大部分文件系统, 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 相关字段计算而来:
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 相关字段计算而来:
available_bytes¶
available_bytes 表示 可用存储空间 ,单位为 字节 ,同样由 statvfs 相关字段计算而来:
注解
文件系统通常为 root 用户预留一部分空间,其他非特权用户不能使用。 对 root 用户,可用空间是 free_bytes ; 对非特权用户,可用空间是 available_bytes 。 一般而言, free_bytes 比 available_bytes 大:
used_bytes_percent¶
used_bytes_percent 表示 可用存储空间百分比 ,计算公式如下:
读者可能觉得奇怪,为什么不是除以 total_bytes 呢? ——因为对于非特权用户,可用存储空间是 available_bytes 而不是 free_bytes 。 对非特权用户来说,总存储空间是:
因此,已用存储空间百分比是针对非特权用户计算的, df 命令也是采用这个口径。
文件节点系列¶
文件节点 系列指标与 存储空间 系列类似,不再赘述。计算公式列举如下:
采集¶
借助 statvfs 系统调用 ,采集某个文件系统使用量统计毫无难度。
可问题是,如何获取所有已挂载的文件系统呢? ——答案是 proc 伪文件系统,内核将所有文件系统挂载点暴露在 /proc/mounts 文件中。
这是一个简单的示例程序,依次展示每个文件系统使用量统计:
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 获取并计算使用量统计:
- 第 30 行,循环遍历每个文件系统挂载点;
- 第 31 行,跳过一些非设备文件系统,如 procfs 伪文件系统;
- 第 35-38 行配合,跳过重复挂载点;
- 第 41 行,调用 statvfs 系统调用,获取文件系统统计值;
- 第 46-59 行,根据上节相关公式计算所有指标;