Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ebpf hello world #153

Open
lzh2nix opened this issue Oct 1, 2022 · 0 comments
Open

ebpf hello world #153

lzh2nix opened this issue Oct 1, 2022 · 0 comments

Comments

@lzh2nix
Copy link
Owner

lzh2nix commented Oct 1, 2022

程序员的世界里第一个程序永远是从hello world 开始. ebpf 也不例外.

在上一篇中讲过 ebpf 编程需要将 ebpf 代码编译成字节码(需要使用llvm编译), 不过在golang的世界里这个就比较简单了,都有比较成熟的实现,我们这里使用 cilium 出的ebpf库(https://github.com/cilium/ebpf), 主要是有cilium 这个宇宙最火的 ebpf项目背书, 稳定性+性能都有保证.

在这里我们就以系统每次调用 sync 的时候打印出, 本次sync被那个程序调用了并且统计调用的耗时为例.

首先我们编写ebpf部分的代码, 当sync 系统调用时打印当前进程的pid以及cmd

// +build ignore

#define __TARGET_ARCH_arm64 1
//#define __TARGET_ARCH_x86 1

#include "../headers/common.h"



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

SEC("tracepoint/syscalls/sys_enter_sync") // sys_enter_sync被调用时需要执行的ebpf程序
int syscall_enter_sync(void *info) {
    u32 pid = bpf_get_current_pid_tgid() >> 32; // 获取当前进程的pid
    char cmd[16] = "";
    bpf_get_current_comm(&cmd, sizeof(cmd)); // 获取当前进程的cmd
    bpf_printk("hello world from my pid = %d, cmd = %s\n", pid,cmd); // 输出到 /sys/kernel/tracing/trace_pipe
    return 0;
}

以上就是一个hello world版本的 ebpf 程序, 这里有几个新概念

  • SEC("XXXXX") 这里说明 syscall_enter_sync 函数和进入sync 系统调用相关联
  • bpf_get_current_pid_tgid() >> 32 获取当前进程的pid
  • bpf_get_current_comm(void*, int) 获取当前进程的cmd

后两个都数据 ebpf 的help function, 都可以在 https://man7.org/linux/man-pages/man7/bpf-helpers.7.html 中查找.
然后通过 go generate 来生成字节码

go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang  bpf sync.c -- -I../headers

下面就来看下怎么在golang中加载上面的ebpf代码

package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/cilium/ebpf/link"
	"github.com/cilium/ebpf/rlimit"
)

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang  bpf sync.c -- -I../headers

func main() {
	// Allow the current process to lock memory for eBPF resources.
	if err := rlimit.RemoveMemlock(); err != nil {
		log.Fatal(err)
	}

	// Load pre-compiled programs and maps into the kernel.
	objs := bpfObjects{}
	if err := loadBpfObjects(&objs, nil); err != nil {
		log.Fatalf("loading objects: %v", err)
	}
	defer objs.Close()

	// Open a tracepoint and attach the pre-compiled program. Each time
	// the kernel function enters, the program will increment the execution
	// counter by 1. The read loop below polls this map value once per
	// second.
	// The first two arguments are taken from the following pathname:
	// tracepoint/syscalls/sys_enter_sync
	kp, err := link.Tracepoint("syscalls", "sys_enter_sync", objs.SyscallEnterSync, nil)
	if err != nil {
		log.Fatalf("opening tracepoint: %s", err)
	}
	defer kp.Close()

	log.Println("waiting ctrl-c for stop...")
	stopper := make(chan os.Signal, 1)
	signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
	<-stopper
}

image

完整代码参考 https://github.com/lzh2nix/go-ebpf-workshop/tree/main/helloworld

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant