摘要: 监控系统在 Linux 系统上获取物理磁盘 IO 以及使用情况的原理, 让我们一起来探索一下
物理磁盘列表和磁盘 IO
逻辑: 通过读取 / proc/diskstats 获得物理磁盘列表以确认哪些是物理设备 (算云硬盘) 以及 iops 等信息, 此目录注解见注 1
条件
此行是 14 列, 使用 sscanf 取到设备名
此行中的设备名 组装成
/sys/block / 设备名 / device
看此文件夹是否存在, 如果存在则是物理磁盘设备
备注 1: 判断文件 / 文件夹是否存在使用函数
access(syspath, F_OK)
, 存在返回 0
备注 2: 如果设备名为中含有 / 的话要转换成!, 如下
- while ((slash = strchr(name, '/'))) {
- *slash = '!';
- }
备注 3: 目录 / sys/block 下的所有子目录代表着系统中当前被发现的所有块设备, 其中的内容已经变为了指向它们在 / sys/devices / 中真实设备的符号链接文件
物理磁盘使用情况 和总量
读取 / etc/mtab 获取设备名和挂载点
/etc/mtab 该文件也是记载当前系统已经装载的文件系统, 包括一些操作系统虚拟文件, 这跟 / etc/fstab 有些不同./etc/mtab 文件在 mount 挂载, umount 卸载时都会被更新, 时刻跟踪当前系统中的分区挂载情况.
核心代码见 读取 / etc/mtab 核心函数
文件内容
对应的含义
驱动器
挂载点
文件系统
读写权限
使用情况计算逻辑:
使用上一节已经拿到的物理磁盘列表
遍历寻找 / etc/mtab 中的分区 (已分区) 和物理磁盘 (直接挂载文件系统) 所挂载的目录
使用 statfs 获得所挂载的目录使用情况确定每个分区的使用情况注 2
根据分区和物理硬盘的关系获得物理硬盘的使用情况
核心代码见计算单个设备的使用情况
在获取使用量情况失败的时候, 可能是因为没有挂载获取其他特殊的情况, 我们就默认使用量为 0, 我们可以根据磁盘名来获取磁盘总量, 见获取物理磁盘总容量
备注 1:/dev/root 设备可以从 / proc/cmdline 中获取到真实设备名
备注 2:rootfs 设备要忽略, 此为根文件系统(内核启动时所 mount 的第一个文件系统)
注
注 1
文件名 /proc/diskstats
文件内容
对应的含义
设备号
编号
设备
读完成次数
合并完成次数
读扇区次数
读操作花费毫秒数
写完成次数
合并写完成次数
写扇区次数
写操作花费的毫秒数
正在处理的输入 / 输出请求数
输入 / 输出操作花费的毫秒数
输入 / 输出操作花费的加权毫秒数. 注 2
计算磁盘的使用量 使用率 和总量的方法
- #include <sys/vfs.h> /* 或者 <sys/statfs.h> */
- int statfs(const char *path, struct statfs *buf);
- int fstatfs(int fd, struct statfs *buf);
- struct statfs
- {
- long f_type; /* 文件系统类型 */
- long f_bsize; /* 经过优化的传输块大小 */
- long f_blocks; /* 文件系统数据块总数 */
- long f_bfree; /* 可用块数 */
- long f_bavail; /* 非超级用户可获取的块数 */
- long f_files; /* 文件结点总数 */
- long f_ffree; /* 可用文件结点数 */
- fsid_t f_fsid; /* 文件系统标识 */
- long f_namelen; /* 文件名的最大长度 */
- };
返回说明:
成功执行时, 返回 0. 失败返回 - 1
statfs 结构中可用空间块数有两种 f_bfree 和 f_bavail, 前者是硬盘所有剩余空间, 后
者为非 root 用户剩余空间, ext3 文件系统给 root 用户分有 5% 的独享空间, 所以这里是不
同的地方. 这里要强调的是每块的大小一般是 4K(* 这句话错误, 不一定都是 4k, 正确做法是: 总大小 = sfs.f_blocks*f_bsize, 即块数 * 每块的大小, 单位是 bytes, 也就是要 / 1024/1024/1024 才是 GB 单位).
读取 / etc/mtab 核心函数
- mount_table = setmntent("/etc/mtab", "r"); // 打开文件系统描述文件的文件名, 并且返回可以被使用的文件指针 getmntent().
- mount_entry = getmntent(mount_table);// 函数读取文件系统的下一行来自文件流的描述文件并返回指向结构的指针(即循环读取文件)
- device = mount_entry->mnt_fsname;
- mount_point = mount_entry->mnt_dir;
- statfs(mount_point, &s) != 0 // 此条件成立时获取成功
- endmntent(mount_table);// 关闭流和与其相关联的文件系统描述文件.
具体用法见 Linux 中 getmntent,setmntent ,endmntent 函数的详细用法
计算单个设备的使用情况
- #define M (1024*1024)
- blocks_used = s.f_blocks - s.f_bfree; // 使用量
- blocks_percent_used = 0;
- if (blocks_used + s.f_bavail)
- {
- blocks_percent_used = blocks_used * 100 / (blocks_used + s.f_bavail); // 使用率
- }
- /* GNU coreutils 6.10 skips certain mounts, try to be compatible. */
- if (strcmp(device, "rootfs") == 0)
- continue;
- Record record;
- record.disk_total_val = CalRound((blocks_used + s.f_bavail) * s.f_bsize, M); // 总量
- record.disk_use_val = CalRound((s.f_blocks - s.f_bfree) * s.f_bsize, M); //
- record.use_precent = blocks_percent_used;
CalRound 四舍五入 见 注 3
获取物理磁盘总容量
在获取使用量情况失败的时候, 可能是因为没有挂载获取其他特殊的情况, 我们就默认使用量为 0, 我们可以根据磁盘名来获取磁盘总量, 以下是核心代码
- unsigned long long AgentDiskRpt::readDiskTotal(const string &deviceName)
- {
- int fd, ret;
- unsigned long long size;
- fd = open(deviceName.c_str(), O_RDONLY);
- if (fd == -1)
- {
- close(fd);
- return -1;
- }
- ret = ioctl(fd, BLKGETSIZE64, &size);
- if (ret == 0) {
- close(fd);
- return CalRound(size,M);
- }
- close(fd);
- return 0;
- }
注 3
CalRound 四舍五入
- unsigned long long CalRound(unsigned long long value, int base)
- {
- unsigned long long ret = 0;
- if (base <= 1)
- return value;
- unsigned long long tmp = base / 2;
- if (tmp <= 0)
- tmp = 1;
- if (value % base>= tmp)
- ret = value / base + 1;
- else
- ret = value / base;
- return ret;
- }
参考
Linux /etc/fstab 和 etc/mtab 有什么区别 http://www.metsky.com/archives/711.html
statfs
Linux 中 getmntent,setmntent ,endmntent 函数的详细用法
来源: https://www.qcloud.com/developer/article/1400809