相比于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
标签:err,bpf,cmd,ebpf,eBPF,内核,ringbuf,go
From: https://www.cnblogs.com/WJQ2017/p/18295349