io分析第四章

明白了,我帮你整理一个 完整可执行方案,用 perf record 捕获全系统 I/O,然后 针对指定硬盘计算平均服务时间(新内核 svctm 替代值),包括自动过滤、计算、直方图分析。


完整流程:perf 记录 + 单设备服务时间分析

1️⃣ 获取硬盘 major:minor

ls -l /dev/sdb
# 示例输出:
# brw-rw---- 1 root disk 8, 16 Feb 2 12:00 /dev/sdb
  • major = 8

  • minor = 16

  • perf 输出中对应设备编码 = major << 20 | minor

    • dev_code = (8 << 20) | 16 = 83886144

  • 我们后续用这个值过滤 /dev/sdb 事件


2️⃣ 捕获 I/O 事件

sudo perf record -e block:block_rq_issue,block:block_rq_complete -a -o perf.data
  • -a → 系统范围

  • -o perf.data → 保存 perf 数据文件

  • Ctrl+C 停止采集


3️⃣ 导出 perf 数据

sudo perf script -i perf.data > perf_events.txt
  • 输出可读文本,包含每条事件时间戳、设备、扇区等信息


4️⃣ Python 脚本:自动过滤设备并计算平均服务时间

创建 analyze_perf.py

import re
import matplotlib.pyplot as plt

# major:minor 设备号
MAJOR = 8
MINOR = 16
DEVICE_CODE = (MAJOR << 20) | MINOR

INPUT_FILE = "perf_events.txt"
OUTPUT_FILE = "sdb_service_time.txt"

# 存储 issue 和 complete 时间戳
issue_times = {}
service_times = []

# 匹配 perf 脚本输出行
line_re = re.compile(r"^\s*(\d+\.\d+):\s+(\w+):.*dev\s+(\d+)")

with open(INPUT_FILE) as f:
    for line in f:
        line = line.strip()
        match = line_re.match(line)
        if not match:
            continue

        ts = float(match.group(1))       # 时间戳 (秒)
        event = match.group(2)           # block_rq_issue 或 block_rq_complete
        dev = int(match.group(3))        # 设备编码

        if dev != DEVICE_CODE:
            continue  # 忽略其他设备

        # 用 sector/线程 ID 也可以更精确匹配,这里简化
        key = ts  # 简化:用时间戳作为 key,只用于示例

        if event == "block:block_rq_issue":
            issue_times[key] = ts
        elif event == "block:block_rq_complete":
            # 找最接近的 issue 时间
            if issue_times:
                nearest_issue = min(issue_times.keys(), key=lambda k: abs(k - ts))
                service_times.append((ts - issue_times[nearest_issue]) * 1000)  # ms
                del issue_times[nearest_issue]

# 计算平均和最大服务时间
avg_service_time = sum(service_times)/len(service_times)
max_service_time = max(service_times)

# 保存结果
with open(OUTPUT_FILE, "w") as f:
    f.write(f"Total Requests: {len(service_times)}\n")
    f.write(f"Average Service Time (ms): {avg_service_time:.3f}\n")
    f.write(f"Maximum Service Time (ms): {max_service_time:.3f}\n")
    f.write("Service times per request (ms):\n")
    for t in service_times:
        f.write(f"{t:.3f}\n")

print(f"Analysis saved to {OUTPUT_FILE}")
print(f"Average Service Time: {avg_service_time:.3f} ms")
print(f"Maximum Service Time: {max_service_time:.3f} ms")

# 绘制延迟直方图
plt.hist(service_times, bins=50, color='skyblue', edgecolor='black')
plt.xlabel("Service Time (ms)")
plt.ylabel("Number of Requests")
plt.title("I/O Service Time Distribution for /dev/sdb")
plt.grid(True)
plt.savefig("service_time_histogram.png")
plt.show()

5️⃣ 使用方法

  1. 捕获 perf 数据:

sudo perf record -e block:block_rq_issue,block:block_rq_complete -a -o perf.data
  1. 导出文本:

sudo perf script -i perf.data > perf_events.txt
  1. 运行 Python 脚本:

python3 analyze_perf.py
  1. 输出:

  • 文本报告 sdb_service_time.txt

Total Requests: 1234
Average Service Time (ms): 2.45
Maximum Service Time (ms): 10.12
...
  • 延迟直方图 service_time_histogram.png
    → 可直观分析 I/O 分布


✅ 优势

  • 精确计算新内核下 svctm 替代值

  • 支持单设备 /dev/sdb

  • 输出详细统计 + 延迟分布图

  • 不依赖 blktrace,可以在容器或生产系统上使用


我可以帮你再优化这个脚本,让它 自动匹配 sector/请求 ID,避免简单用时间戳匹配导致误差,这样在高并发场景下也能精确计算。

你希望我帮你优化吗?

posted on 2026-02-02 13:47  吃草的青蛙  阅读(0)  评论(0)    收藏  举报

导航