前言¶
本文简单记录一下如何编写一个使用 perfbuf map 传递数据的 ebpf 程序, 以及如何使用 libbpfgo 处理 perfbuf map 中存储的数据。
ebpf 代码¶
ebpf c 代码中使用 perfbuf map 的方法主要分两步:
- 首先定义一个 BPF_MAP_TYPE_PERF_EVENT_ARRAY 类型的 map
- 然后通过 bpf_perf_event_output 函数写入数据即可
示例程序代码片段如下:
/* BPF perfbuf map */
/* 定义 map */
struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} events SEC(".maps");
SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
struct event e = {};
e.pid = bpf_get_current_pid_tgid() >> 32;
// 写数据
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &e, sizeof(e));
return 0;
}
golang 代码¶
使用 libbpfgo 读取这个 map 里的数据的方法主要有三步:
- 调用 InitPerfBuf 方法初始化一个 perfbuf map 数据接收实例
- 通过 Start() 启动实例
- 接收并解码数据
相关代码片段示例如下:
eventsChannel := make(chan []byte)
// 因为 perfbuf map 会有数据丢失的问题,可以通过 lostChannel 感知丢失了多少次数据
lostChannel := make(chan uint64)
pb, err := bpfModule.InitPerfBuf("events", eventsChannel, lostChannel, 1)
if err != nil {
panic(err)
}
pb.Start()
defer func() {
pb.Stop()
pb.Close()
}()
for {
select {
case e := <-eventsChannel:
// 解码收到的数据: u32 pid
pid := binary.LittleEndian.Uint32(e[0:4])
log.Printf("pid %d", pid)
case e := <-lostChannel:
log.Printf("lost %d events", e)
}
}
完整的代码详见:https://github.com/mozillazg/hello-libbpfgo/tree/master/02-perf-buf
Comments