基础题1:什么是eBPF?
参考答案: eBPF 是 Linux 内核提供的一套虚拟机机制,允许用户编写小程序,安全地挂载到内核各个钩子点执行。 早期叫BPF,只用于网络包过滤;eBPF是增强版,现在可以追踪系统调用、内核函数、网络、性能观测。
关键点:
- 程序跑在内核上下文,但不需要改内核源码、不用编译内核模块;
- 加载前内核有验证器verifier,会检查循环、边界,防止死循环、越界访问,保证内核安全;
- 用户态和内核态通过 map 做数据交换;
- 内核5.x版本完善,现在广泛用于观测、网络、安全。
通俗比喻:内核身上预留很多插座,eBPF小程序就是安全的小插件,插上去就能看/统计事件,不用拆开改造内核本身。
基础题2:BPF 和 eBPF 的区别?
参考答案:
- BPF(经典cBPF):老版本,指令集简单,只能做网络数据包过滤,寄存器少,能力有限。
- eBPF:extended‑BPF,扩展指令集,更多寄存器,支持循环、函数调用;钩子点不再局限网络,可以挂系统调用、kprobe跟踪内核函数、tracepoint跟踪埋点、perf事件等。 现在生产环境基本都用eBPF。
原理题3:eBPF完整工作流程是什么?(高频)
参考答案:
- 用户态写eBPF小程序(C语言子集);
- 用clang编译成eBPF字节码;
- 用户态工具(bpftool / libbpf)把字节码加载进内核;
- 内核Verifier验证器校验字节码:检查是否越界、会不会死循环、非法操作;验证失败直接拒绝加载,保护内核;
- 验证通过后,JIT编译器把字节码编译成本地机器码;
- 将程序挂载到钩子点(kprobe/tracepoint/syscall钩子/XDP等);
- 内核事件触发时自动运行eBPF程序;
- eBPF程序把统计数据写到BPF Map;
- 用户态程序从Map读取数据展示结果。
⚠️重点必说:验证器verifier + JIT + Map 三个核心组件。
原理题4:什么是 BPF Map?有哪些常见类型
参考答案: Map 是 eBPF 提供的数据存储,是用户态 ↔ eBPF内核程序交换数据的唯一媒介。 内核eBPF程序不能直接操作用户态内存,全部通过Map读写。
常见Map类型:
- hash哈希表:key‑value统计,统计PID、系统调用次数,最常用;
- array数组:固定大小数组;
- ring buffer / perf buffer:事件推送,把事件实时发给用户态(比如捕获每次系统调用事件);
- lpm_trie:最长前缀匹配,多用于网络;
- stack_trace:栈回溯,采集调用栈,做性能分析。
注意:Map有最大大小限制,内核态不能随便无限分配内存。
原理题5:kprobe、tracepoint、XDP分别是什么钩子,区别?
参考答案:
- kprobe :动态探测内核任意函数入口/出口。
- 优点:可以探测几乎任意内核函数;
- 缺点:内核函数没有ABI稳定保证,内核版本一变函数名字变了,程序直接失效。
- tracepoint :内核预先埋好的静态埋点,内核开发者预留。
- 优点:ABI稳定,内核升级接口不变,生产更可靠;
- 缺点:只有内核预先定义好的点位才能用,不能随便钩任意函数。
- XDP(eXpress Data Path) :网络包钩子,数据包还没进入内核网络协议栈之前就执行eBPF。 可以直接丢包、重定向、修改报文,性能极高,用于高性能防火墙、负载均衡。
面试话术:优先生产尽量用tracepoint;排查问题临时调试用kprobe;网络加速用XDP。
场景题6:eBPF能做什么?结合你熟悉的后端/存储场景
参考答案(结合你自研KV、IO、内存背景,背这个):
- 性能观测、故障排查 跟踪系统调用:read/write/open/io_uring_enter,统计每个进程调用频次、耗时; 跟踪内存分配:malloc、jemalloc、page分配;看KV存储的IO延迟、系统调用耗时。
- 网络:XDP做防火墙、负载均衡、流量统计。
- 安全审计:监控进程exec、fork,审计谁在执行命令。
- 内核旁路优化:比如服务网格Cilium用eBPF替代iptables。
- 跟踪锁、调度:看线程阻塞、协程底层系统调用行为。
结合自己项目话术:我做高性能KV存储的时候,可以用eBPF观测io_uring系统调用的延迟分布,看是用户态慢还是内核IO慢;也可以跟踪malloc/free看内存碎片、jemalloc行为。
场景题7:eBPF和传统工具strace、perf有什么区别?(非常爱考)
参考答案:
- strace:ptrace实现。ptrace每次系统调用发生都会暂停目标进程,进程被打断,开销很大,高并发服务直接降性能;适合小进程调试,不适合线上观测。
- perf:硬件PMU + tracepoint,perf采样; perf是采样,不是全量捕获;高频率事件会丢失,适合统计趋势。
- eBPF:程序在内核运行,事件触发直接执行逻辑,不需要暂停用户进程;可以做全量统计;但是eBPF程序本身也有CPU开销。
简单对比:
- strace:全量捕获,但开销巨大,线上慎用;
- perf:采样,开销低,会丢事件;
- eBPF:可全量可采样,在内核执行,不打断进程;但受verifier限制,编写难度更高。
面试坑点:要说清楚 strace底层ptrace会stop进程,对高并发服务伤害很大,线上一般不用strace。
难点题8:eBPF有哪些限制?
参考答案:
- eBPF C是C语言子集,不能调用普通libc函数,只能调用内核提供的helper帮助函数;不能直接循环无限循环,verifier会拒绝。
- verifier验证器限制:循环必须有确定上界;不能无边界访问内存;复杂逻辑写起来很别扭。
- kprobe不稳定:内核升级,内核函数名字、实现一改,kprobe直接失效;生产优先tracepoint。
- eBPF程序执行本身占用CPU,如果钩子事件非常频繁(百万次/秒),eBPF逻辑复杂会带来额外CPU开销。
- Map容量有限,不能无限存数据,要做淘汰;ring‑buffer满了会丢事件。
- 需要较高版本内核,老内核3.x/4.x支持很差。
难点题9:eBPF用户态和内核态怎么传递数据?能不能直接指针互访?
参考答案: 不能直接互相访问对方内存,内核态eBPF不能解引用用户态裸指针。 唯一正规方式是 BPF Map ; 如果要访问用户态内存,需要调用专门helper函数 bpf_probe_read_user() 拷贝用户内存到内核。
坑点:直接解引用用户指针会被verifier拒绝加载。
对比题10:eBPF 和内核模块ko对比?
参考答案:
- 内核模块ko:可以任意修改内核,写错直接内核panic崩溃;需要匹配内核版本,编译对应内核头文件。
- eBPF:有verifier安全校验,非法操作直接拒绝,不会轻易panic;不需要编译内核模块; 但是功能受限制,不能随便修改内核任意结构;复杂操作做不了。
结论:优先eBPF做观测统计;真正修改内核逻辑才考虑模块。
开放性问题11:如果让你用eBPF排查自研KV存储的性能抖动,你怎么做?
参考答案(贴合你的背景,口述版)
- 先看抖动来自哪里:是网络、io_uring系统调用、磁盘IO、内存分配锁、调度阻塞。
- 使用tracepoint钩住io_uring相关tracepoint,统计io_uring_enter调用耗时分布;
- 跟踪read/write系统调用,统计每个IO的延迟;
- 跟踪jemalloc/malloc相关函数,看内存分配有没有锁等待、频繁分配释放;
- 用stack_trace map采集抖动时刻的调用栈,定位是用户态代码、协程调度还是内核阻塞;
- 通过ring buffer把事件输出到用户态,画出延迟直方图,找到长尾延迟来源。
容易踩坑的错误回答(避雷)
- ❌错误:eBPF可以随便写C语言,直接调用printf/malloc ✅正确:eBPF只能用内核提供helper,没有libc,不能直接malloc,内存只能用map。
- ❌错误:kprobe是稳定接口,线上随便用 ✅正确:kprobe钩内核函数,内核版本一变就挂;生产优先tracepoint。
- ❌错误:eBPF完全没有性能开销 ✅正确:高频事件下eBPF逻辑会增加CPU;事件过多ring‑buffer会丢包丢事件。
- ❌错误:eBPF可以直接访问用户态指针 ✅正确:必须用bpf_probe_read_user拷贝。
快速记忆提纲(面试前快速过一遍)
eBPF四件套:字节码、Verifier验证器、JIT、BPF‑Map 三大钩子:kprobe动态、tracepoint静态稳定、XDP网络前置 和strace/perf区别:ptrace会暂停进程;perf采样丢事件;eBPF内核内执行不暂停进程 限制:C子集、verifier约束、kprobe不稳定、内核版本依赖、有CPU开销
参考链接 :0voice · GitHub