eBPF 用户态和内核态基于ringbuf交互

相比于perf_event_array,ringbuf优势在于
1. 读取数据是有序的,即提交顺序和消费顺序保持一致。
2. 避免数据复制,即提交数据到map和传递到用户态都不需要拷贝。

内核态 ringbuf/ringbuf.c

#include "../headers/vmlinux.h"
#include "../headers/bpf_endian.h"
#include "../headers/bpf_helpers.h"

#define TC_ACT_OK 0

struct {
	__uint(type, BPF_MAP_TYPE_RINGBUF);
	__uint(max_entries, 100);
    __uint(pinning, LIBBPF_PIN_BY_NAME);
} events SEC(".maps");

#ifndef memcpy
#define memcpy(dest, src, n)   __builtin_memcpy((dest), (src), (n))
#endif

char __license[] SEC("license") = "Dual MIT/GPL";

SEC("tc")
int test_ringbuf(struct __sk_buff *skb) {
    char str[10] = "abc";
    char *res = bpf_ringbuf_reserve(&events, 10, BPF_ANY);
    if (res) {
        memcpy(res, str, 10);
        bpf_ringbuf_submit(res, BPF_ANY);
    }
    return TC_ACT_OK;
}

用户态cmd/main.go

package main

import (
	"os"
	"os/exec"
	"os/signal"
	"syscall"

	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/ringbuf"
	"github.com/cilium/ebpf/rlimit"
	"k8s.io/klog/v2"
)

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf ../ringbuf/ringbuf.c

func SetupSignalHandler() (stopCh <-chan struct{}) {
	stop := make(chan struct{})
	c := make(chan os.Signal, 2)
	signal.Notify(c, []os.Signal{os.Interrupt, syscall.SIGTERM}...)
	go func() {
		<-c
		close(c)
		close(stop)
	}()

	return stop
}

func DelTcEbpf() {
	cmds := []string{
		"tc qdisc del dev ens33 clsact",
	}
	for _, cmd := range cmds {
		exec.Command("bash", "-c", cmd).CombinedOutput()
	}
}

func main() {
	stopCh := SetupSignalHandler()

	DelTcEbpf()
	defer DelTcEbpf()

	// eBPF先删再加
	cmds := []string{
		"tc qdisc add dev ens33 clsact",
		"tc filter add dev ens33 egress bpf da obj ringbuf/ringbuf.o sec tc",
	}
	for i, cmd := range cmds {
		if _, err := exec.Command("bash", "-c", cmd).CombinedOutput(); err != nil && i > 0 {
			klog.Errorf("exec %s failed, err is %v", cmd, err)
			return
		}
	}

	// Allow the current process to lock memory for eBPF resources.
	if err := rlimit.RemoveMemlock(); err != nil {
		klog.Errorf("rlimit remove memory lock failed, err is %v", err)
		return
	}

	eventsMap, err := ebpf.LoadPinnedMap("/sys/fs/bpf/tc/globals/events", nil)
	if err != nil {
		klog.Errorf("load pinned map events failed, err is %v", err)
		return
	}
	defer eventsMap.Close()

	reader, err := ringbuf.NewReader(eventsMap)
	if err != nil {
		klog.Errorf("new ringbuf reader failed, err is %v", err)
		return
	}
	defer reader.Close()

	go func() {
		for {
			if record, err := reader.Read(); err != nil {
				klog.Errorf("reader read failed, err is %v", err)
				return
			} else {
				klog.Infof("reader read %s", record.RawSample)
			}
		}
	}()

	<-stopCh
}

bash run-ebpf-test.sh

rm -f ringbuf/ringbuf.o
rm -f cmd/bpf_bpfeb.go cmd/bpf_bpfeb.o cmd/bpf_bpfel.go cmd/bpf_bpfel.o cmd/ebpf-test
clang -c ringbuf/ringbuf.c -o ringbuf/ringbuf.o -target bpf -O2 -g
go generate ./cmd
go build -o ./cmd/ebpf-test ./cmd/.
./cmd/ebpf-test

posted on 2024-07-11 08:59  王景迁  阅读(6)  评论(0编辑  收藏  举报

导航