EBPF是否只能通过kprobe实现Linux内核函数的监控?
监控Linux内核函数的BPF方法不止kprobe
当然不是只能用kprobe来监控任意内核函数,除了kprobe,还有几种BPF追踪技术可以实现内核函数监控,以下是常见的几种:
1. kretprobe
和kprobe配套使用,专门用来监控内核函数的返回阶段,可以获取函数的返回值。kprobe监控函数入口,kretprobe监控函数出口,两者结合能完整覆盖函数的执行周期。
用BCC实现的简单示例:
#!/usr/bin/python3 from bcc import BPF from time import sleep bpf_program = """ int test_kretprobe(void *ctx) { // 通过PT_REGS_RC获取函数返回值 int ret_val = PT_REGS_RC(ctx); bpf_trace_printk("getpid返回值: %d\\n", ret_val); return 0; } """ b = BPF(text=bpf_program) b.attach_kretprobe(event="__x64_sys_getpid", fn_name="test_kretprobe") while 1: sleep(10) b.trace_print()
2. fentry/fexit
这是Linux 5.5版本引入的高效追踪机制,直接在内核函数的入口/出口位置插入BPF程序,不需要像kprobe那样依赖断点指令,性能损耗更低,同时避免了kprobe可能存在的某些稳定性问题。
需要注意的是,使用fentry/fexit要求内核版本≥5.5,BCC中可以通过attach_fentry和attach_fexit方法实现:
#!/usr/bin/python3 from bcc import BPF from time import sleep bpf_program = """ int test_fentry(void *ctx) { bpf_trace_printk("进入getpid函数\\n"); return 0; } """ b = BPF(text=bpf_program) b.attach_fentry(event="__x64_sys_getpid", fn_name="test_fentry") while 1: sleep(10) b.trace_print()
3. Tracepoints
内核预定义的追踪点,属于稳定的官方接口,不会因为内核版本迭代导致函数名、参数结构变化。但tracepoints是内核预先定义好的,只能监控那些内核提供了追踪点的函数,无法覆盖所有内核函数。
比如监控系统调用入口的tracepoint示例:
#!/usr/bin/python3 from bcc import BPF from time import sleep bpf_program = """ int test_tracepoint(void *ctx) { bpf_trace_printk("触发getpid系统调用\\n"); return 0; } """ b = BPF(text=bpf_program) # 绑定sys_enter_getpid追踪点 b.attach_tracepoint(tp="syscalls:sys_enter_getpid", fn_name="test_tracepoint") while 1: sleep(10) b.trace_print()
总结
如果需要监控任意内核函数,kprobe/kretprobe、fentry/fexit是更合适的选择——它们可以作用于所有导出的内核函数(非导出函数虽然也能通过地址绑定,但稳定性差,不推荐)。而tracepoints适合对稳定性要求高,但不需要覆盖所有函数的场景。
内容的提问来源于stack exchange,提问作者ray
相关产品推荐
相关产品推荐

