You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用bcc捕获指定用户进程触发的块设备写操作相关信息

解决方案

一、关联回写IO与原始进程的实现思路

异步缓存回写场景下IO由内核线程触发,无法直接在block_rq_complete中拿到原始进程PID,可以通过两级追踪实现关联:

  • 在内核将页标记为脏的节点(比如account_page_dirtied函数)插入探测点,记录<struct page *地址, 进程PID/进程名>的映射关系存入BPF哈希表
  • 在block_rq_complete触发时,遍历请求对应的bio中的page,查表获取写入该页的原始进程信息,即可将swapper/flush线程执行的回写IO关联到原始发起进程

二、无关事件过滤方案

可以在eBPF代码侧提前做三层过滤,大幅减少无效事件上报:

  • 设备号过滤:block_rq_complete tracepoint自带dev参数,可提前获取目标SSD的主从设备号(通过lsblk命令查询),仅处理对应设备的IO
  • 操作类型过滤:通过args->cmd_flags判断请求类型,仅上报写操作,丢弃读、discard等无关请求
  • 进程过滤:关联到原始PID后,仅上报你指定关注的进程ID/进程名对应的IO,其余直接丢弃

三、修正后的示例代码

注意:该代码需要内核支持读取bio中的page结构,测试前请确认目标内核版本兼容,部分内核版本写请求宏需要替换为REQ_OP_WRITE

from bcc import BPF
import sys

# 传入要监控的块设备号(比如8:0对应sda,设备号为0x800)和目标进程PID(可选)
TARGET_DEV = int(sys.argv[1], 16) if len(sys.argv) >1 else 0x800
TARGET_PID = int(sys.argv[2]) if len(sys.argv) >2 else 0

PROG = f"""
#include <linux/blkdev.h>
#include <linux/sched.h>
#include <linux/bio.h>

struct data_t {{
    u32 original_pid;
    u64 ts;
    u64 sector_start;
    u64 nr_sectors;
    char comm[TASK_COMM_LEN];
    char original_comm[TASK_COMM_LEN];
}};
BPF_PERF_OUTPUT(events);
// 哈希表存储脏页对应的原始进程信息
BPF_HASH(page_pid_map, struct page *, u32);
BPF_HASH(page_comm_map, struct page *, char[TASK_COMM_LEN]);

// 探测脏页标记函数,记录页对应的进程信息
int trace_account_page_dirtied(struct pt_regs *ctx, struct page *page) {{
    u32 pid = bpf_get_current_pid_tgid() & 0xFFFFFFFF;
    char comm[TASK_COMM_LEN] = {{0}};
    bpf_get_current_comm(comm, sizeof(comm));
    page_pid_map.update(&page, &pid);
    page_comm_map.update(&page, comm);
    return 0;
}}

TRACEPOINT_PROBE(block, block_rq_complete) {{
    // 第一层过滤:只处理目标设备的请求
    if (args->dev != {TARGET_DEV}) {{
        return 0;
    }}
    // 第二层过滤:只处理写请求
    if (!(args->cmd_flags & REQ_WRITE)) {{
        return 0;
    }}
    struct data_t data = {{}};
    u32 *original_pid;
    char (*original_comm)[TASK_COMM_LEN];
    
    data.ts = bpf_ktime_get_ns();
    data.sector_start = args->sector;
    data.nr_sectors = args->nr_sector;
    bpf_get_current_comm(&data.comm, sizeof(data.comm));
    
    // 遍历bio获取page查原始进程
    struct request *rq = (struct request *)args->rq;
    struct bio *bio = rq->bio;
    while (bio != NULL) {{
        struct bio_vec bvec;
        struct bvec_iter iter;
        bio_for_each_segment(bvec, bio, iter) {{
            struct page *page = bvec.bv_page;
            original_pid = page_pid_map.lookup(&page);
            if (original_pid != NULL) {{
                data.original_pid = *original_pid;
                original_comm = page_comm_map.lookup(&page);
                if (original_comm != NULL) {{
                    __builtin_memcpy(&data.original_comm, original_comm, sizeof(data.original_comm));
                }}
                // 第三层过滤:如果指定了目标PID,只上报对应进程的IO
                #if {TARGET_PID} != 0
                if (data.original_pid != {TARGET_PID}) {{
                    goto next;
                }}
                #endif
                events.perf_submit(args, &data, sizeof(data));
                // 一个请求只要匹配到目标进程就上报,避免重复提交
                goto end;
            }}
        }}
        next:
        bio = bio->bi_next;
    }}
    end:
    return 0;
}}
"""

def print_event(cpu, data, size):
    event = b["events"].event(data)
    print(f"原始进程:{event.original_comm.decode()}({event.original_pid}) 执行进程:{event.comm.decode()} 时间戳:{event.ts} 起始扇区:{event.sector_start} 扇区数:{event.nr_sectors}")

b = BPF(text=PROG)
# 挂载脏页追踪点
b.attach_kprobe(event="account_page_dirtied", fn_name="trace_account_page_dirtied")
b["events"].open_perf_buffer(print_event)
print(f"监控设备号0x{TARGET_DEV:x} 目标PID:{TARGET_PID if TARGET_PID !=0 else '全部'},开始采集...")
while True:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

四、使用说明

  1. 先通过lsblk -o NAME,MAJ:MIN查询目标SSD的主从设备号,比如sda的MAJ:MIN是8:0,转换为16进制设备号就是0x800
  2. 启动脚本时传入设备号和可选的目标PID即可,比如监控sda设备上PID为1234的进程的写IO:python3 script.py 0x800 1234

内容的提问来源于stack exchange,提问作者Tez_Nikka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 18:39:03