如果你正在处理Linux bcc,先别急着照搬网上的参数。线上MySQL写入突然慢了5倍,iostat显示%util接近100%,但磁盘型号是NVMe,理论IOPS不应该被压满。这是半年前我在某电商大促前夜遇到的真实现场。当时没有strace、没有perf,更不敢重启——最后是靠bcc工具链里的biolatency和biosnoop,在10秒内锁定了是SATA端口驱动层的一个DMA重试导致单次IO延迟从50μs飙升到800ms。本文不讨论理论,只记录真实可复用的排查路径。
为什么传统的iostat+iotop不够用?
验证与回滚
iostat只能看到设备层的平均延迟和队列长度,但无法区分是寻道时间、设备内部磨损均衡还是SCSI命令重试导致的尖刺。iotop依赖proc文件系统采样,对短时突发延迟完全无能为力。而bcc/BPFTrace利用eBPF在内核中动态插桩,以纳秒级精度记录每次IO的完整生命周期,且对生产环境零侵入。
Linux bcc:bcc/BPFTrace工具链概览
先看关键判断
bcc(BPF Compiler Collection)和BPFTrace是Linux eBPF技术的两个主流前端。bcc提供Python和C接口,适合编写复杂工具;BPFTrace是单行脚本式工具,语法类似awk,适合快速现场排查。定位I/O延迟最常用的几个工具:
- biosnoop:逐个打印磁盘IO请求,包含进程、设备、扇区、延迟
- biolatency:输出IO延迟的直方图,快速识别异常分布
- fileslower:过滤出同步文件写入(如fsync)中延迟超过阈值的调用
- ext4slower / xfsslower:定位文件系统层延迟
实战:定位I/O高延迟的四个步骤
1. 先用biolatency判断延迟分布是否异常
连接到目标服务器,直接运行:
biolatency -d 10 1
参数说明:-d表示磁盘设备号过滤(可选),后面两个数字分别是采样间隔秒数和次数。输出是一个ASCII直方图,横轴为延迟范围(μs/ms),纵轴为IO次数。正常情况下NVMe盘延迟集中在10-100μs,如果看到大量IO落在1ms以上甚至10ms+,就存在明显异常。
关键判断:如果高延迟区间集中在1-4ms,很可能是闪存GC或磨损均衡;如果出现几十ms甚至秒级的尾巴,大概率是上层锁竞争或驱动重试。
2. 用biosnoop抓取高延迟IO的细节
在另一终端执行:
biosnoop -Q
-Q标记会在每次IO完成时打印完整信息,包括进程名、PID、设备、扇区偏移、大小和延迟。重点关注延迟超过阈值的行。例如输出中某行:
mysqld 12345 sda 128 8 512 0.804
意味着mysqld进程在sda设备上发出了一个128扇区(64KB)的写请求,延迟804ms。接下来可以交叉验证——这个扇区是否处于文件系统的日志区域?是否有其他进程同时大量写入?用iostat -x 1确认是否出现await远大于svctm的情况(表示队列等待)。
3. 下探到块设备层:区分是队列等待还是设备响应慢
biosnoop只能看到块设备层的请求生命周期,但延迟成分由三部分组成:排队延迟(I/O调度器等待)、驱动层延迟(SCSI命令传输)、设备延迟(物理盘处理)。要拆分它们,需要借助更高精度的BPFTrace脚本。以下脚本追踪块设备请求从创建到完成的时间戳:
#!/usr/bin/bpftrace
#include <linux/blkdev.h>
kprobe:blk_start_request {
@start[arg0] = nsecs;
}
kretprobe:blk_complete_request {
if (@start[arg0]) {
$dur = (nsecs - @start[arg0]) / 1000;
@usecs = hist($dur);
if ($dur > 100000) {
printf("High delay: %d usn", $dur);
}
delete(@start[arg0]);
}
}
注意:生产环境执行前,务必确认内核版本支持kprobe且没有安全策略禁止。运行后若频繁出现百万微秒级的延迟,且设备等待时间(从blk_start到blk_complete)集中在驱动层,可进一步追踪SCSI命令的响应时间。
风险提示:采样高频率kprobe可能增加CPU开销,建议在低峰期或限制采样范围(如只监控特定设备)。
4. 文件系统层跟踪:排查日志写入/脏页回刷相关的延迟
很多I/O延迟并非来自数据块,而是来自fsync日志提交。使用fileslower -d 100可以捕获所有同步文件写入中延迟超过100ms的调用,输出包含进程名、文件路径(如果内核支持)、延迟和字节数。如果是MySQL的InnoDB日志文件(ib_logfile*)频繁出现高延迟,则可能是磁盘刷写策略或NVMe的Power Loss Protection缓冲区耗尽。
相关阅读:此处可内链到“Linux bcc常见问题”专题。
补充参考:此处可内链到“Linux bcc故障排查实例”。
常见根因对照表
配置前的检查
| 现象 | bcc/BPFTrace指标 | 可能根因 |
|---|---|---|
| 延迟集中在1-4ms,周期性出现 | biolatency直方图有固定小峰 | SSD内部垃圾回收(GC) |
| 延迟偶发>100ms,设备层延迟明显 | biosnoop显示单次请求延迟高 | NVMe驱动错误恢复或Flash通道故障 |
| 延迟与文件日志写入同步出现 | fileslower显示fsync延迟高 | 文件系统日志设备繁忙或写缓存策略不当 |
| 延迟伴随后台进程(如kworker)活动 | biosnoop显示kworker发起的IO延迟高 | 内核内存回收(kswapd)触发的脏页回刷 |
验证与回滚
实际操作要点
找到疑似根因后,不要急着重启或更换硬件。先做一次回退验证:如果是设备驱动问题,可尝试echo mq-deadline > /sys/block/sda/queue/scheduler更换I/O调度器,观察延迟是否改善。如果是文件系统日志导致的,可调整commit=60挂载参数(仅限ext4)延长提交周期,但需理解数据丢失风险。确认根因后,再制定永久修复方案(如升级固件、更换硬盘、调整内核参数)。
关联教程:此处可内链到“Linux bcc部署与验证”内容。
Linux bcc:小结
故障定位思路
bcc/BPFTrace工具链让运维人员可以在毫秒级粒度上透视I/O路径的每个环节,从块设备层到文件系统层,甚至跟踪单个进程的读写延迟。上述四个步骤构成了一个高效定位管道:先宏观直方图,再微观抓包,然后拆分延迟成分,最后针对性验证。掌握这套方法后,I/O高延迟不再是个黑盒。
最后提醒一句:在生产环境运行eBPF程序前,务必确认内核配置 CONFIG_BPF=y、CONFIG_BPF_EVENTS=y,并检查SELinux或AppArmor是否允许bpf调用。建议先在预发布环境演练。把这些步骤跑通后,Linux bcc基本就能稳定落地。
延伸阅读
