Bootchart解析 - proc_diskstats.log

本文详细介绍了Linux系统中Bootchart生成的proc_diskstats.log文件的含义和解析方法。proc_diskstats.log记录了磁盘状态,包括读写次数、时间等统计信息,用于性能分析。通过pybootchartgui源码解析,提取关键指标如读写吞吐量和利用率,帮助优化系统I/O性能。

主要针对Linux系统的Bootchart文件进行分析,关于怎么采集Bootchart可以查看官方文档。


前言

本文主要针对Bootchart生成的proc_diskstats.log文件进行分析
Bootchart参考文章:elinux-Bootchart


一、proc_diskstats.log文件含义

顾名思义,proc_diskstats主要代表磁盘状态,当开启采集bootchart时,会在/data/bootchart/目录下生成proc_diskstats.log文件。这个文件用于显示磁盘、分区和统计信息,比起常规的cat /proc/diskstats 会增加一行采集信息的时间。

z3q:/ # cat /proc/diskstats
   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       1 ram1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       2 ram2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       3 ram3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       4 ram4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       5 ram5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       6 ram6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       7 ram7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       8 ram8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       9 ram9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      10 ram10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      11 ram11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      12 ram12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      13 ram13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      14 ram14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      15 ram15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       0 loop0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       1 loop1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       2 loop2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       3 loop3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       4 loop4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       5 loop5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       6 loop6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       7 loop7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       0 sda 9267 171 938604 3934 13 0 56 6 0 1850 4220 0 0 0 0
   8       1 sda1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       2 sda2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       3 sda3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       4 sda4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       5 sda5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       6 sda6 9264 171 938580 3933 7 0 56 5 0 1850 4220 0 0 0 0
   8      16 sdb 7610 16 159552 2050 300 534 29696 550 0 880 2780 0 0 0 0
   8      17 sdb1 111 0 900 53 3 0 24 2 0 20 70 0 0 0 0
   8      18 sdb2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8      19 sdb3 7497 16 158636 1996 285 534 29672 545 0 860 2710 0 0 0 0

proc_diskstats.log文件样式如下,其中507、528、549即为采集时间:

507
   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1       1 ram1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  ···
   1      14 ram14 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   1      15 ram15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   7       0 loop0 35 1 3080 16 0 0 0 0 0 12 20 0 0 0 0
  ···
   7     120 loop15 10 0 80 4 0 0 0 0 0 4 4 0 0 0 0
 254       0 zram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8       0 sda 5190 4845 255144 5611 16 0 80 8 0 984 5556 0 0 0 0
   8       1 sda1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  ···
 259      18 sda34 1979 2543 89608 3023 3 0 24 0 0 260 3388 0 0 0 0
   8      16 sdb 5 0 40 22 0 0 0 0 0 24 24 0 0 0 0
   8      17 sdb1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   8      32 sdc 5 0 40 6 0 0 0 0 0 8 8 0 0 0 0
   8      48 sdd 5 0 40 3 0 0 0 0 0 4 4 0 0 0 0
   8      64 sde 66 1 1080 42 0 0 0 0 0 28 32 0 0 0 0
  ···
   8      80 sdf 5 0 40 4 0 0 0 0 0 0 0 0 0 0 0
  ···
   8      90 sdf10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  31       0 mtdblock0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 252       0 dm-0 3311 0 105200 4568 0 0 0 0 0 380 4568 0 0 0 0
  ···
   7     128 loop16 12 0 96 5 0 0 0 0 0 8 8 0 0 0 0
 252      15 dm-15 6 0 48 4 0 0 0 0 0 4 4 0 0 0 0
   7     136 loop17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 252      16 dm-16 8 0 64 4 0 0 0 0 0 4 4 0 0 0 0

528
   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
   ···
   8       0 sda 5236 4845 255512 5632 16 0 80 8 0 1012 5596 0 0 0 0
   ···
 252      20 dm-20 13 0 104 12 0 0 0 0 0 4 12 0 0 0 0

549
   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

二、proc_diskstats.log文件解析

1. 参数解释

cat /proc/diskstats具体参数可以参考[Linux 运维 – 存储] /proc/diskstats详解www.kernel.org

以sda举例

# sda
  8       0 sda 9267 171 938604 3934 13 0 56 6 0 1850 4220 0 0 0 0
# 第1列为 为设备号 - 8
# 第2列为 次设备号 - 0
# 第3列为 设备名称 - sda
# 第4列为 成功完成读的总次数 - 9267
# 第5列为 合并读次数 - 171 (为了效率可能会合并相邻的读和写,从而两次4K的读在它最终被处理到磁盘上之前可能会变成一次8K的读,才被计数(和排队),因此只有一次I/O操作。)
# 第6列为 读扇区的次数 - 938604 (读扇区大小,成功读过的扇区总次数)
# 第7列为 读花的时间(ms) - 3934

# 第8~11分别是写, 与4~7读对应, 依次是 13 0 56 0

# 第12列为 I/O的当前进度 - 0 (只有这个域应该是0,如果这个值为0,同时write_complete read_complete io_processing 一直不变可能就就是IO hang了)
# 第13列为 输入输入花的时间(ms) - 1850 (花在I/O操作上的毫秒数,这个域会增长只要field 就不为0)
# 第14列为 输入/输出操作花费的加权毫秒数 - 4220

# 第15列为 丢弃完成的成功数 - 0
# 第16列为 丢弃合并数 - 0
# 第17列为 丢弃扇区数 - 0
# 第18列为 丢弃花费时间(ms) - 0
# quote https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
What:		/proc/diskstats
Date:		February 2008
Contact:	Jerome Marchand <jmarchan@redhat.com>
Description:
		The /proc/diskstats file displays the I/O statistics
		of block devices. Each line contains the following 14
		fields:

		==  ===================================
		 1  major number
		 2  minor mumber
		 3  device name
		 4  reads completed successfully
		 5  reads merged
		 6  sectors read
		 7  time spent reading (ms)
		 8  writes completed
		 9  writes merged
		10  sectors written
		11  time spent writing (ms)
		12  I/Os currently in progress
		13  time spent doing I/Os (ms)
		14  weighted time spent doing I/Os (ms)
		==  ===================================

		Kernel 4.18+ appends four more fields for discard
		tracking putting the total at 18:

		==  ===================================
		15  discards completed successfully
		16  discards merged
		17  sectors discarded
		18  time spent discarding
		==  ===================================

		Kernel 5.5+ appends two more fields for flush requests:

		==  =====================================
		19  flush requests completed successfully
		20  time spent flushing
		==  =====================================

		For more details refer to Documentation/admin-guide/iostats.rst

2. 文件解析

查看pybootchartgui源码,可以看到关于diskstats的解析,下面给出关键代码

def _parse_proc_disk_stat_log(file, numCpu):
    """
    Parse file for disk stats, but only look at the whole device, eg. sda,
    not sda1, sda2 etc. The format of relevant lines should be:
    {major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq}
    """
    disk_regex_re = re.compile ('^([hsv]d.|mtdblock\d|mmcblk\d|cciss/c\d+d\d+.*)$')

    # this gets called an awful lot.
    def is_relevant_line(linetokens):
        if len(linetokens) < 14:
            return False
        disk = linetokens[2]
        return disk_regex_re.match(disk)

    disk_stat_samples = []

    for time, lines in _parse_timed_blocks(file):
        sample = DiskStatSample(time)
        relevant_tokens = [linetokens for linetokens in map (lambda x: x.split(),lines) if is_relevant_line(linetokens)]

        for tokens in relevant_tokens:
            disk, rsect, wsect, use = tokens[2], int(tokens[5]), int(tokens[9]), int(tokens[12])
            sample.add_diskdata([rsect, wsect, use])

        disk_stat_samples.append(sample)

    disk_stats = []
    for sample1, sample2 in zip(disk_stat_samples[:-1], disk_stat_samples[1:]):
        interval = sample1.time - sample2.time
        if interval == 0:
            interval = 1
        sums = [ a - b for a, b in zip(sample1.diskdata, sample2.diskdata) ]
        readTput = sums[0] / 2.0 * 100.0 / interval
        writeTput = sums[1] / 2.0 * 100.0 / interval
        util = float( sums[2] ) / 10 / interval / numCpu
        util = max(0.0, min(1.0, util))
        disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util))

    return disk_stats
  • is_relevant_line 函数是找出相关的sda、sdb、hda、vda、mtdblock0、mmcblk0等,忽略ram、sda1等
disk_regex_re = re.compile ('^([hsv]d.|mtdblock\d|mmcblk\d|cciss/c\d+d\d+.*)$')
disk = 'sda'
print(disk_regex_re.match(disk))
<re.Match object; span=(0, 3), match='sda'>
disk = 'sda1'
print(disk_regex_re.match(disk))
None
disk = 'mmcblk0'
print(disk_regex_re.match(disk))
<re.Match object; span=(0, 7), match='mmcblk0'>
  • _parse_timed_blocks 这个函数是将proc_diskstats.log进行分割,_parse_timed_blocks(file)返回一个列表,元素为元组形式,第一个元素为时间,第二个元素为按行读取的列表。
# 仅作示意
[(507, ['1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0','1       1 ram1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0'],
 [528, ['1       0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0'])]
  • 接下来解析出所有磁盘(sda\sdb\sdc所有的)本次采样时间的采样时间time、设备名disk、删除读取次数rsect、扇区写入次数wsect、 输入输出花的时间use
  • 两次采样差值为变化量
interval = sample1.time - sample2.time  # 采样时间差,ms
readTput = sums[0] / 2.0 * 100.0 / interval  # sums[0] 为两次采样读扇区次数差
writeTput = sums[1] / 2.0 * 100.0 / interval  # sums[1] 为两次采样写扇区次数差
util = float( sums[2] ) / 10 / interval / numCpu  # sums[2] 为两次采样读写时间差
util = max(0.0, min(1.0, util))
disk_stats.append(DiskSample(sample2.time, readTput, writeTput, util))

扇区大小为512字节
最终获得的disk_status

<think>好的,我现在需要分析用户提供的代码片段,并理解他们的问题。用户之前讨论了AI推理中的缓冲区管理,特别是双目模式下缓冲区的独立分配问题。现在用户给出了一段配置`proc_cfg`的代码,并希望得到关于`WORKBUF`的配置是否正确的确认。 首先,我需要仔细阅读用户提供的代码片段,检查每个缓冲区的配置是否正确。用户提到的三个缓冲区:`WORKBUF`、`RONLYBUF`和`INPUTBUF`,但代码中主要配置了`WORKBUF`、`RONLYBUF`和`model_buf`。用户可能想知道他们是否正确地将`WORKBUF`的物理地址、虚拟地址和大小赋值给了`proc_cfg`的相应字段。 接下来,我需要确认代码中的结构是否符合预期。例如,`proc_cfg.proc_mem.buf[AI3_PROC_BUF_WORKBUF]`的配置是否从`p_net->io_mem`获取了正确的值。根据之前的讨论,每个`net_path`应该有独立的内存分配,因此需要确保`p_net`指向当前通道的私有内存结构。 另外,用户可能存在的疑问是,`model_buf`是否应该指向`proc_mem`,而`RONLYBUF`是否正确地使用了`intl_mem`。需要检查这些配置是否与硬件或库的要求一致,例如某些库可能要求模型参数存储在特定的缓冲区中。 还需要检查是否有遗漏的配置,比如`INPUTBUF`是否在代码中被正确设置。用户提供的代码片段中没有看到`INPUTBUF`的配置,这可能是一个潜在的问题,尤其是在双目模式下需要独立分配输入缓冲区的情况下。 此外,需要验证各个缓冲区的大小是否足够,地址是否正确映射,避免出现地址冲突或越界访问的情况。例如,`WORKBUF`的大小是否满足模型中间数据的存储需求,`RONLYBUF`是否足够容纳模型参数。 最后,总结用户的问题,可能需要指出代码中的正确部分和需要改进的地方,比如补充`INPUTBUF`的配置,确保所有缓冲区独立分配,并验证地址和大小的正确性。</think>### **1. 配置代码分析** 以下是最终的 `proc_cfg` 缓冲区配置逻辑: ```c // 配置 WORKBUF(绑定当前通道的 io_mem) proc_cfg.proc_mem.buf[AI3_PROC_BUF_WORKBUF].pa = p_net->io_mem.pa; proc_cfg.proc_mem.buf[AI3_PROC_BUF_WORKBUF].va = p_net->io_mem.va; proc_cfg.proc_mem.buf[AI3_PROC_BUF_WORKBUF].size = p_net->io_mem.size; // 配置 model_buf(绑定当前通道的 proc_mem) proc_cfg.model_buf.pa = p_net->proc_mem.pa; proc_cfg.model_buf.va = p_net->proc_mem.va; proc_cfg.model_buf.size = p_net->proc_mem.size; // 配置 RONLYBUF(绑定当前通道的 intl_mem) proc_cfg.proc_mem.buf[AI3_PROC_BUF_RONLYBUF].pa = p_net->intl_mem.pa; proc_cfg.proc_mem.buf[AI3_PROC_BUF_RONLYBUF].va = p_net->intl_mem.va; proc_cfg.proc_mem.buf[AI3_PROC_BUF_RONLYBUF].size = p_net->intl_mem.size; ``` --- ### **2. 关键配置验证** #### **(1) `WORKBUF` 的正确性** - **绑定来源**:`p_net->io_mem` - 通常通过 `network_alloc_io_buf(net_path, size)` 分配,用于存储模型推理的中间数据(如特征)。 - **验证点**: - **独立性**:`p_net` 指向当前通道(`net_path`)的私有内存,确保双目模式下左右眼通道的 `WORKBUF` 地址不同。 - **地址有效性**:需通过 `mmap()` 或 IOMMU 映射物理地址(`pa`)到虚拟地址(`va`),确保访问权限正确。 #### **(2) `model_buf` 的作用** - **用途**: - 存储模型的 **核心参数**(如权重、结构描述符),可能与 `RONLYBUF` 功能重叠。 - 部分硬件要求模型参数与中间数据(`WORKBUF`)分离,需根据硬件文档确认是否需要独立分配。 - **潜在问题**: - 若 `model_buf` 和 `RONLYBUF` 指向同一内存,可能导致冲突(如模型加载时覆盖配置数据)。 #### **(3) `RONLYBUF` 的配置** - **绑定来源**:`p_net->intl_mem` - 通常通过 `network_alloc_intl_buf(net_path, size)` 分配,用于存储只读数据(如量化参数、锚框)。 - **验证点**: - **只读性**:需确保硬件或软件逻辑不会修改该缓冲区内容,避免触发写保护异常。 --- ### **3. 遗漏的 `INPUTBUF` 配置** 当前代码未包含 `INPUTBUF` 的绑定逻辑,需补充以下配置(否则可能导致输入数据访问错误): ```c // 补充 INPUTBUF 配置(绑定当前通道的 input_mem) proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].pa = p_net->input_mem.pa; proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].va = p_net->input_mem.va; proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].size = p_net->input_mem.size; ``` - **风险提示**: - 若 `INPUTBUF` 未绑定私有内存,双目模式下左右眼输入数据会互相覆盖,导致推理结果异常。 --- ### **4. 双目模式下的独立性验证** #### **验证步骤** 1. **打印缓冲区地址** 在 `network_open()` 结尾打印各缓冲区地址: ```c printf("net_path=%u: WORKBUF va=0x%p, RONLYBUF va=0x%p, INPUTBUF va=0x%p\n", net_path, p_net->io_mem.va, p_net->intl_mem.va, p_net->input_mem.va); ``` - **预期输出**: ``` net_path=0: WORKBUF va=0x7fa4259000, RONLYBUF va=0x7fa427a000, INPUTBUF va=0x7fa4249000 net_path=1: WORKBUF va=0x7fa425a000, RONLYBUF va=0x7fa427b000, INPUTBUF va=0x7fa424a000 ``` 2. **检查地址重叠** - 若 `net_path=0` 和 `net_path=1` 的缓冲区地址相同,则存在共享冲突,需修复分配逻辑。 --- ### **5. 修复建议** #### **步骤1:确保 `INPUTBUF` 独立绑定** - 修改 `network_open()`,补充 `INPUTBUF` 的绑定逻辑: ```c proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].pa = p_net->input_mem.pa; proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].va = p_net->input_mem.va; proc_cfg.proc_mem.buf[AI3_PROC_BUF_INPUTBUF].size = p_net->input_mem.size; ``` #### **步骤2:分离 `model_buf` 与 `RONLYBUF`** - 若硬件要求 `model_buf` 与 `RONLYBUF` 独立,需分配额外内存: ```c // 修改 model_buf 的绑定来源(假设 g_model_mem 为全局模型参数内存) proc_cfg.model_buf.pa = g_model_mem.pa; proc_cfg.model_buf.va = g_model_mem.va; proc_cfg.model_buf.size = g_model_mem.size; ``` #### **步骤3:动态调整缓冲区大小** - 根据模型信息动态分配 `WORKBUF` 和 `RONLYBUF`: ```c // 从模型信息中获取需求大小 UINT32 workbuf_size = model_info.proc_mem.buf[AI3_PROC_BUF_WORKBUF].size; UINT32 ronly_size = model_info.proc_mem.buf[AI3_PROC_BUF_RONLYBUF].size; // 分配缓冲区 network_alloc_io_buf(net_path, workbuf_size); network_alloc_intl_buf(net_path, ronly_size); ``` --- ### **6. 总结** - **正确部分**: - `WORKBUF` 和 `RONLYBUF` 绑定到当前通道的私有内存,符合双目模式需求。 - **待修复部分**: - **遗漏 `INPUTBUF` 配置**:需补充绑定逻辑以避免输入数据冲突。 - **`model_buf` 与 `RONLYBUF` 的潜在冲突**:需根据硬件文档确认是否需独立分配。 - **验证手段**:打印缓冲区地址并检查双目通道的独立性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值