Preface¶
This article briefly documents how to write an ebpf program that uses a ringbuf map to transfer data, and how to use libbpfgo to process data stored in the ringbuf map.
ebpf codes¶
The way ringbuf map is used in ebpf c code is in three main steps:
- Define a BPF_MAP_TYPE_RINGBUF map
- Before writing data, apply for memory space through bpf_ringbuf_reserve, and write data only after the application is successful, if this step is missing, the program execution will prompt libbpf: load bpf program failed: Invalid argument error.
- write data via the bpf_ringbuf_submit function.
The sample code snippet is as follows:
/* BPF ringbuf map */
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024 /* 256 KB */);
} events SEC(".maps");
SEC("kprobe/do_sys_openat2")
int kprobe__do_sys_openat2(struct pt_regs *ctx)
{
struct event *e;
// xx
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) {
return 0;
}
e->pid = bpf_get_current_pid_tgid() >> 32;
// xx
bpf_ringbuf_submit(e, 0);
return 0;
}
golang codes¶
There are three main steps to read the data in this map using libbpfgo:
- Call the InitRingBuf method to initialize a ringbuf map data receiving instance.
- Start the instance with Start()
- Receive and decode data
The sample code snippet is as follows:
eventsChannel := make(chan []byte)
pb, err := bpfModule.InitRingBuf("events", eventsChannel)
if err != nil {
panic(err)
}
pb.Start()
defer func() {
pb.Stop()
pb.Close()
}()
for {
select {
case e := <-eventsChannel:
// decode data: u32 pid
pid := binary.LittleEndian.Uint32(e[0:4])
log.Printf("pid %d", pid)
}
}
You can check out full codes on Github: https://github.com/mozillazg/hello-libbpfgo/tree/master/03-ring-buf
Comments