本文是对于 Eunomia Tutorials 2 的一个学习记录
什么是 kprobe
Kprobe(Kernel Probe)是 Linux 内核提供的一项强大功能,它允许开发者和系统管理员在不修改内核源代码或重启系统的前提下,在任意内核函数处动态插入“探针”:
- 工作原理:通过临时替换目标函数的前几条指令为一个断点指令(如 int3)
- 执行流程:当程序执行到断点时,CPU 控制权会交给 kprobe 系统
- 事件回调:系统执行注册的回调函数,完成数据采集后恢复原函数执行
- 两种类型:
- Kprobe:在函数入口处执行
- Kretprobe:在函数返回时执行
这种机制为我们提供了零侵入式的内核行为洞察能力,特别适用于实时监控、性能分析和故障排查等场景。
do_unlinkat 的作用
do_unlinkat 是 Linux 内核中的一个内部函数,它的作用是执行文件或目录的删除操作。其在内核源码中的定义如下:
| |
do_unlinkat是内核中实际执行文件删除逻辑的最终汇聚点- 用户空间调用
unlink()、unlinkat()或rmdir()等系统调用时,最终都会通过系统调用表路由到这个函数 - 采用文件描述符 (AT_FDCWD) 和路径名的组合方式,提供了灵活的路径解析能力
vmlinux.h
不同内核版本之间,内核数据结构如结构体字段位置、字段名称等都可能发生变化。传统的 eBPF 程序直接使用内核头文件会导致:
- 兼容性问题:程序在不同内核版本中崩溃
- 字段偏移错误:读取到无效内存数据
- 维护困难:需要针对每个内核版本进行适配
vmlinux.h 利用内核的BTF(BPF Type Format) 信息生成与当前运行内核完全匹配的类型定义:
| |
内核态程序 kprobe_unlink.bpf.c
为 do_unlinkat 函数同时设置入口探针和返回探针:
- 函数入口:
- 捕获调用进程的 PID
- 获取要删除的文件名
- 函数返回:
- 捕获调用进程的 PID
- 获取函数返回值(删除操作的结果)
| |
用户态 Go 程序
用户态程序负责三个主要任务:
- 环境初始化:
- 移除 eBPF 的内存限制
- 加载编译好的 eBPF 程序
- 探针附加:
- 将 kprobe 绑定到
do_unlinkat - 将 kretprobe 绑定到
do_unlinkat的返回点
- 将 kprobe 绑定到
- 日志处理:
- 读取内核的跟踪日志
- 筛选并显示与文件删除相关的事件
| |
编译与运行
Makefile:
| |
运行后会得到类似的结果:
