eBPF程序加载失败主因是未预编译为.o字节码或内核版本不兼容;必须用clang -O2 -target bpf编译,确保helpers(如bpf_get_current_comm)在目标内核可用,并正确指定ProgramType。

为什么用 bpf.NewProgram 加载 eBPF 程序失败?
常见错误是直接传入未编译的 C 源码,或忽略了 target kernel 版本兼容性。eBPF 程序必须提前编译为 .o(ELF object),且需匹配目标机器的内核头文件版本。Go 侧不能“现场编译”,只能加载已验证的字节码。
- 用
clang -O2 -target bpf -c trace_exec.c -o trace_exec.o编译,注意加-O2(否则 verifier 拒绝) - 确保
trace_exec.c中调用的 helpers(如bpf_get_current_pid_tgid())在目标内核中可用(5.2+ 才支持bpf_get_current_comm()安全读取进程名) - 加载时指定
Type: ebpf.ProgramTypeTracePoint或ebpf.ProgramTypeKprobe,别错用SocketFilter
如何用 ebpf.Program.AttachKprobe 监控 execve 并提取进程参数?
execve 是最敏感的异常入口点,但 kprobe 无法直接读取用户栈上的 argv。必须在 eBPF 程序里用 bpf_probe_read_user_str() 分段读取,且要处理页缺失和字符串截断。
- eBPF C 侧:从
ctx->regs[1](argv指针)开始读,最多取前 3 个参数(避免超限),用#pragma unroll展开循环 - Go 侧 attach 时用
"/sys/kernel/debug/tracing/events/syscalls/sys_enter_execve"更稳定(tracepoint 比 kprobe 更少干扰) - 别依赖
ctx->regs[0]判断返回值——kprobe 在 entry 阶段触发,此时还没执行,应结合sys_exit_execve的返回码做二次过滤
怎么把 eBPF map 中的进程数据实时同步到 Go 主程序?
eBPF map(如 ebpf.MapTypeHash)不是通道,不能直接阻塞读;频繁轮询 Map.Lookup 会拖慢性能,而 Map.Poll 只对 perf event map 有效。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 推荐组合:eBPF 端用
perf_event_arraymap +bpf_perf_event_output()推送结构体,Go 端用perf.NewReader实时消费 - 结构体字段必须对齐(
__u64 pid; __u32 uid; char comm[16];),且comm要用bpf_get_current_comm()填充,不能 strcpy - Go 读 perf event 时需设置
PerfEventAttr.SampleType = perf.SampleTime | perf.SampleRaw,否则拿不到完整 payload
检测到异常进程后,为什么 kill -STOP 失败?
eBPF 本身不能直接 kill 进程,它只负责检测和通知。常见误区是以为在 eBPF 程序里调用 bpf_override_return() 就能拦截 execve——这仅适用于极少数内核函数(如 security_bprm_check),且需开启 CONFIG_BPF_KPROBE_OVERRIDE。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法:Go 主程序监听 perf event,收到可疑
comm(如 “curl” 从 /tmp 启动)后,用os.FindProcess(pid).Signal(syscall.SIGSTOP)干预 - 注意权限:Go 进程需有
CAP_SYS_PTRACE或以 root 运行,否则kill会报operation not permitted - 不要在 perf event handler 里做耗时操作(如 HTTP 上报),用 channel 转发到 worker goroutine 处理
真正难的是进程上下文还原——eBPF 拿不到父进程名、当前工作目录、命令行完整参数,这些得靠 Go 侧用 /proc/[pid]/ 补全,而 /proc 在容器里可能被挂载隔离,得提前判断是否在 PID namespace 中。

















