Go脚本获取磁盘IO返回异常设备名及空值问题求助
我正在开发一个Go脚本,用于获取磁盘I/O操作数据,但遇到了脚本返回空值的异常问题。为排查问题,我添加了fmt.Print(deviceName)来检查发现的设备,得到如下日志:
/dev/dm-02023/11/09 16:49:47 IOCounters for /dev/dm-0 not found /dev/sda22023/11/09 16:49:47 IOCounters for /dev/sda2 not found /dev/sda12023/11/09 16:49:47 IOCounters for /dev/sda1 not found
令我困惑的是,日志中出现了异常的设备名(比如/dev/dm-02023),请问有人能解释这些设备名出现的原因吗?以及如何准确获取磁盘I/O操作数据?
我的Go代码如下:
package disk import ( "fmt" "log" "github.com/shirou/gopsutil/disk" ) func GetAllDiskIO() (string, error) { diskPartitions, err := disk.Partitions(false) if err != nil { return "", fmt.Errorf("error during request: %v", err) } var result string for _, partition := range diskPartitions { deviceName := partition.Device diskIO, err := disk.IOCounters(deviceName) if err != nil { log.Printf("error during request for %s: %v", deviceName, err) continue } if counters, ok := diskIO[deviceName]; ok { result += fmt.Sprintf("%s: Read Count: %d, Write Count: %d; ", deviceName, counters.ReadCount, counters.WriteCount) } else { log.Printf("IOCounters for %s not found", deviceName) } } return result, nil }
是否有人遇到过类似问题?或者对这些异常设备名的成因有见解?恳请指导如何改进脚本以准确捕获磁盘I/O操作数据!
异常设备名的成因
你看到的/dev/dm-02023这类异常名称,根本原因是fmt.Print(deviceName)没有加换行符。fmt.Print会直接输出内容到标准输出,而后面的log.Printf会输出带时间戳的日志,两者没有分隔,导致设备名和日志的时间戳直接拼接在一起,看起来像是设备名异常。比如/dev/dm-0加上2023/11/09...就变成了/dev/dm-02023...,并非设备名本身有问题。
无法获取I/O数据的原因及解决方案
1. disk.Partitions(false)参数限制
调用disk.Partitions(false)时,参数false仅返回挂载了文件系统的分区,但IOCounters通常需要的是磁盘设备(如/dev/sda)而非分区(如/dev/sda1),部分逻辑卷或未挂载设备也会被过滤,导致后续查找不到对应I/O数据。
解决办法:
- 若需要磁盘级别的I/O数据,直接调用
disk.IOCounters()(不带参数)获取所有设备的统计; - 若需基于分区查询,先将分区路径转换为对应磁盘设备(比如从
/dev/sda1提取/dev/sda),再去I/O结果中匹配。
2. IOCounters返回键名的匹配问题
当传入单个设备名调用disk.IOCounters(deviceName)时,返回的map键名可能不含/dev/前缀(比如系统内部用sda而非/dev/sda),导致用完整路径deviceName查找时命中失败。
解决办法:
- 调用无参的
disk.IOCounters()获取全量数据,遍历结果时自行过滤需要的设备; - 对设备名做前缀处理,比如去掉
/dev/后再与map键匹配。
3. 改进后的代码示例
package disk import ( "fmt" "log" "strings" "github.com/shirou/gopsutil/disk" ) func GetAllDiskIO() (string, error) { // 获取所有磁盘设备的I/O统计数据 diskIO, err := disk.IOCounters() if err != nil { return "", fmt.Errorf("error getting I/O counters: %v", err) } var result string for device, counters := range diskIO { // 过滤掉不需要的设备(如loop虚拟设备),可根据需求调整 if !strings.HasPrefix(device, "sd") && !strings.HasPrefix(device, "dm-") { continue } // 补充/dev/前缀,让输出更直观 fullDevice := "/dev/" + device result += fmt.Sprintf("%s: Read Count: %d, Write Count: %d; ", fullDevice, counters.ReadCount, counters.WriteCount) } return result, nil }
4. 日志输出修正
之前的fmt.Print(deviceName)导致日志拼接混淆,建议替换为带换行和时间戳的日志输出:
log.Printf("Checking device: %s", deviceName)
内容的提问来源于stack exchange,提问作者kekw

