当前位置: 首页 > news >正文

BCC脚本执行链路

一条 BCC 脚本为什么能跑进内核?把执行链路一次讲清

很多人第一次用 BCC,都会有一种“像在变魔术”的感觉:明明只是运行了一段 Python 脚本,怎么就能监听系统调用、抓内核事件,甚至把内核里的数据实时打印出来?

如果把 BCC 的工作过程拆开看,它其实并不神秘。核心链路只有 6 步:启动用户态脚本、编译内嵌 eBPF 代码、通过bpf()加载进内核、借助perf_event_open()挂到事件上、在内核态执行、最后再把数据送回用户态。把这条链路看清楚,BCC 的运行机制也就通了。

一、先看全链路:BCC 到底做了什么

BCC(BPF Compiler Collection)最大的价值,不是发明了一套新的内核机制,而是把 eBPF 开发中原本琐碎、底层、容易踩坑的步骤包装起来,让工程师可以更快写出可运行的工具。

它的典型执行链路可以概括成下面这样:

用户态脚本启动 -> 内嵌 C 代码 -> Clang/LLVM 编译成 eBPF 字节码 -> 调用 bpf() 加载进内核 -> Verifier 校验通过 -> 通过 perf_event_open() 挂到目标事件 -> 事件发生时触发 eBPF 程序 -> 写入 BPF Map / perf buffer -> 用户态读取结果并输出

如果你把 BCC 看成一个“eBPF 开发加速器”,它的定位就很清楚了:用户态负责控制流程,内核态负责快速执行,BCC 负责把两边顺起来。

二、第一步:启动脚本,准备用户态控制逻辑

BCC 工具通常从一个 Python 脚本开始,比如:

sudopython3 opensnoop# 注意:opensnoop的名字和平台相关,比如Ubuntu平台安装bpfcc-tools包,那么它的名字会是opensnoop-bpfcc

这一步表面上只是“运行脚本”,本质上是在启动一段用户态控制程序。它会完成几件事:

  • 导入bcc
  • 准备内嵌的 eBPF C 代码
  • 设置挂载目标、过滤条件和输出逻辑
  • 准备和内核交换数据所需的 Map 或缓冲区

所以,BCC 脚本并不只是“展示结果”的壳,它本身就是整个 eBPF 工具链的控制平面。

三、第二步:把内嵌 C 代码编译成 eBPF 字节码

在很多 BCC 脚本里,你都会看到一大段字符串形式的 C 代码。那部分代码并不是给 Python 运行的,而是给 Clang/LLVM 编译的。

当执行到BPF(text=bpf_text)这类逻辑时,BCC 会调用编译工具链,把这段 C 代码即时编译成eBPF 字节码

这一步的价值在于,它让开发者可以直接用更熟悉的 C 语法描述内核态逻辑,而不用手写底层字节码。

不过这里要特别说明一点:BCC 的这套动态编译机制,并不等同于 CO-RE。BCC 更常见的做法,是依赖当前系统的内核头文件在本机完成编译,所以它更像“针对当前内核环境即时适配”,而不是“一次编译到处运行”。

四、第三步:通过bpf()把程序送进内核,并先过 Verifier

编译完成后,用户态的 BCC 库会调用bpf(BPF_PROG_LOAD, ...),把刚刚生成的 eBPF 字节码送入 Linux 内核。

但这还不是“送进去就能跑”。在真正加载成功之前,内核还会先让它过一遍Verifier

Verifier 的职责很明确:确保这段程序不会破坏内核安全。它会重点检查这些问题:

  • 是否存在非法内存访问
  • 是否可能出现越界读写
  • 是否存在不安全的指针操作
  • 是否有可能导致不可控执行路径

只有校验通过,程序才会真正被内核接受。随后,JIT 编译器还会把 eBPF 字节码转换成当前 CPU 的本地机器指令,以获得接近原生代码的执行效率。

所以你可以把这一阶段理解成:先审,再上岗

五、第四步:通过perf_event_open()把程序挂到事件上

程序加载进内核后,还不能凭空运行。它必须先“接到某个触发点上”,也就是挂到具体事件上。

这时,BCC 会自动处理很多底层细节,其中最关键的一个系统调用就是perf_event_open()

它通常承担这几件事:

  • 创建用于监听目标事件的 perf event fd
  • 把已加载的 eBPF 程序绑定到这个 fd 上
  • 通过ioctl(..., PERF_EVENT_IOC_ENABLE, ...)激活事件

这就是为什么很多基于 BCC 的工具,表面上看在用 eBPF,底层却总会和 perf 扯上关系。因为很多追踪和采样类场景,本来就是借助 perf 的事件基础设施来触发 eBPF 程序的。

六、第五步:事件一发生,eBPF 就在内核里快速执行

当目标事件真的发生时,比如某个进程调用了openat(),或者某个 tracepoint 被触发,已经挂载好的 eBPF 程序就会在内核态被快速拉起执行。

在这个阶段,eBPF 程序通常会做三类事:

  • 从上下文中提取关键信息,比如 PID、进程名、时间戳、文件名
  • 根据业务逻辑做过滤、统计或聚合
  • 把结果写入 BPF Map 或 perf buffer

这也是 eBPF 之所以适合做观测和性能分析的原因:它离事件发生点很近,运行路径很短,额外开销相对可控。

七、第六步:用户态再把结果读出来并格式化展示

内核态程序跑完之后,结果并不会自己出现在屏幕上。最后一步,还是要回到用户态来做消费和展示。

BCC 用户态程序一般会通过两种方式读取结果:

  • 轮询 BPF Map,读取统计状态
  • 监听 perf buffer,把内核态推送出来的事件流拉回来

拿到原始数据之后,Python 脚本再去做格式化处理,比如:

  • 把时间戳转成人类可读格式
  • 把 PID、进程名、文件路径拼成完整输出
  • 将结果打印到终端,或者转发到监控系统

所以从整体上看,BCC 的输出并不是“eBPF 直接打印出来”的,而是内核态负责采集,用户态负责解释和展示

八、为什么 BCC 总和 perf 一起出现?

eBPF 和 perf 在 Linux 内核里关系很深,至少体现在两个维度。

1. perf 是很多 eBPF 程序的触发通道

很多性能分析和追踪类程序,并不能自己主动运行,而是要依附某个事件源。perf 提供的perf_events基础设施,刚好承担了这个角色。

当你在 BCC 或 bpftrace 里写了一个基于采样、硬件计数器、Kprobe 或 Tracepoint 的程序时,底层通常都是 perf 在负责监听事件,而 eBPF 只是被挂在这个事件源上执行。

2. perf 也是一条高性能的数据回传通道

eBPF 运行在内核态,想把采集到的大量结构化数据传回用户态,常见方式之一就是通过 perf 环形缓冲区。

这时,内核态程序会调用bpf_perf_event_output()把数据写进去;用户态的 BCC 进程再通过 mmap 方式去读取,从而实现低开销的数据传输。

如果你看到BPF_MAP_TYPE_PERF_EVENT_ARRAY,基本就可以把它理解成:这是 eBPF 借助 perf 往用户态送数据的一条高速公路。

写在最后

如果你站在工程实现的角度再回头看 BCC,会发现它真正厉害的地方,不是把 eBPF 变简单了,而是把“编译、加载、挂载、采集、回传、展示”这条原本分散的链路,整理成了一套可直接上手的开发体验。

所以,BCC 不是魔法,也不是黑盒。它只是替你把复杂的底层接口封装好了,让你可以把更多精力放在“我要观察什么、统计什么、定位什么问题”上。

把这条执行链路看明白之后,再去看opensnoopexecsnoopbiolatency这类工具,你就会更容易理解:它们表面是脚本,背后其实都是同一套 eBPF 运行模型。

http://www.jsqmd.com/news/1092801/

相关文章:

  • 反思与自我改进:Agent自我批评、经验学习与技能库构建的闭环
  • SetDPI:3步掌握Windows命令行DPI调整的终极方案
  • 智能插件本地化:3步实现Obsidian全界面中文的终极方案
  • 深入解析MSP-GANG430量产编程器底层协议与DLL API开发指南
  • MTEX工具箱:材料科学家必备的晶体学纹理分析利器
  • 3步实现Gmail账号自动化生成:告别繁琐手动注册的Python解决方案
  • LeetCode 复杂度论证:主定理的推导与算法分析实战
  • Python+pytest集成Jira实现测试自动化与RPA流程
  • 专业硬件调试:AMD Ryzen处理器底层参数调优实战指南
  • TVS管实战选型指南:从关键参数到电路防护设计
  • 【课程设计/毕业设计】基于 SpringBoot+Vue 的考勤数据统计分析系统 企业员工日常出勤管控服务平台设计与实现【附源码、数据库、万字文档】
  • 信用卡拒付率高达83%?ChatGPT Plus国内订阅的5大支付陷阱,金融级风控专家亲授合规替代方案
  • C#异或加密:轻量级数据混淆方案原理与工程实践
  • 三分钟快速上手:哔咔漫画下载器终极指南,打造个人永久漫画库
  • HOG+SVM:从特征提取到行人检测的经典实践
  • iOS应用无源码加固实战:二进制保护与运行时安全防护
  • Ubuntu 22.04 LTS 上为 ThinkPad X1 Carbon 解锁指纹登录:从驱动失效到完美启用的全记录
  • 企业级应用逻辑漏洞挖掘实战:从越权访问到业务安全防御
  • 百考通降重不扭曲原意,降AI不牺牲逻辑
  • 即插即用 | 重塑跨维度交互,GAM注意力机制在ResNet上的实战优化(附完整代码)
  • 鼎阳示波器软件选件权限深度解析与升级实践
  • 移动端API签名逆向实战:从抓包到算法还原的完整方法论
  • 实战指南——Ren‘Py游戏资源rpa解包与脚本rpyc反编译全流程
  • 揭秘Windows系统优化的3个神奇技巧:让你的电脑重获新生
  • Steam Deck双系统切换终极指南:告别复杂设置,3分钟搞定多系统引导
  • 无需编程,快速打造专属物联网APP——ThingsCloud平台实战指南
  • 哪些专业的保研率最高
  • 免费开源镜像烧录工具Balena Etcher终极指南:安全快速制作系统启动盘
  • 使用Cobra静态扫描工具精准检测PHP WebShell漏洞实战指南
  • Spring AI 1.0 GA发布:Java开发者如何用“全家桶”方式构建Agent