当前位置:首页>Linux>搞懂 Linux 内核 BPF 技术,才算真正吃透 Linux 内核底层

搞懂 Linux 内核 BPF 技术,才算真正吃透 Linux 内核底层

  • 2026-06-28 11:49:59
搞懂 Linux 内核 BPF 技术,才算真正吃透 Linux 内核底层

大家好,我是蟹老板~

如果把 Linux 内核的发展史拉出来看,我个人觉得有几个节点特别关键。

一个是内核模块(LKM)的出现。

一个是容器技术的成熟。

再往后,就是 BPF

很多人第一次接触 BPF,大概率是在 Kubernetes 圈子里听说 Cilium。或者是在性能排查时,看见过 bpftrace、BCC 这些工具。

当时我也是。

第一次看到有人不用改内核、不重启机器、不加载第三方内核模块,就能实时观察系统调用、网络包流转路径、CPU 调度行为的时候,我脑子里就一个想法:

这玩意儿是不是开挂了?

你说它是网络技术吧,它能跑 XDP,能做转发,能做限流,能做负载均衡。

你说它是观测技术吧,它能挂 kprobe、tracepoint、uprobe,一边跑业务一边看内核函数怎么走。

你说它是安全技术吧,它又能接 LSM,能审计进程行为,能盯系统调用,甚至可以在运行时拦一刀。

这就很离谱。

一、BPF 是什么?

1.1 什么是 BPF?

BPF 全称是 Berkeley Packet Filter(伯克利数据包过滤器),看名字就知道,最早是干网络包过滤的。1992 年就出来了,比很多读者年纪都大。tcpdump 底层用的就是最早的经典 BPF,也就是大家常说的 cBPF

那时候的 BPF 功能很单一,就是在内核里跑过滤规则,符合条件的包才往用户态送,省掉了不必要的内存拷贝,提升抓包效率。搁当年这已经是黑科技了,但放在今天看,能干的事实在有限,就像个只能干单一活的流水线工人。

转折点在 2014 年,Alexei Starovoitov 对 BPF 做了彻头彻尾的改造,扩展成了通用的内核执行引擎,也就是现在的 eBPF。它不再局限于网络包过滤,能挂载到内核的各个位置,网络、追踪、安全、性能分析,啥都能干。相当于在内核里塞了个安全的虚拟机,你写好程序塞进去,就能动态扩展内核的功能。

我13年刚入行的时候,想给内核加个统计功能,得翻内核源码,改代码,重新编译,重启服务器。一套流程下来大半天没了,线上环境根本不敢这么玩。有了 eBPF 之后,程序写好,一条命令加载进去,立刻生效,热插拔一样,连业务都不用停。

这就是 BPF 的厉害之处,不是让你无限制地改内核,而是在“不能把内核搞挂”的前提下,让你参与内核决策

1.2 演进之路:cBPF 与 eBPF 核心差异

cBPF 和 eBPF 是两代东西,名字沾了点边,底层差老远了。

二、BPF 核心原理

2.1 BPF 虚拟机

BPF 程序本质上跑在一个寄存器虚拟机上。跟 JVM 这种通用虚拟机不一样,BPF 虚拟机是高度受限的。指令集是精简过的,只能做算术运算、内存访问、跳转、调用辅助函数这些操作。不能随便访问内核内存,不能随便调用内核函数,所有操作都在规则边界内。

这个虚拟机有 11 个 64 位寄存器。R0 是返回值寄存器,R1-R5 是参数寄存器(函数调用时传参用),R6-R9 是通用寄存器(调用函数时内容保持不变),R10 是只读的栈帧指针。还有一个隐式的程序计数器(PC),你只能跳转到相对于 PC 的偏移量。

栈空间是 512 字节固定大小。512 字节啊兄弟们,写惯了用户态程序动不动就 malloc 几 MB 的人,第一次看到这个限制的时候内心是崩溃的。但没办法,内核里资源金贵,512 字节够你放几个局部变量了。

BPF 程序不是直接跑在硬件上的。内核里有一个 BPF 解释器,负责执行 BPF 字节码。但大多数情况下,BPF 程序会走 JIT(Just-In-Time)编译 路径,把字节码编译成宿主机的原生机器指令,执行效率接近原生内核代码。

2.2 BPF 验证器(Verifier)

验证器这东西,是 BPF 最核心的组件,没有之一。所有 BPF 程序加载之前,必须过验证器的安检,通不过的一律不准跑。

验证流程大概是这样的:

  1. 1. 检查指令合法性:是不是有效的BPF指令,有没有越界跳转。
  2. 2. 检查内存访问:访问的内存地址是不是合法的,是不是在栈范围内,或者是不是通过Helper函数获取的安全指针。
  3. 3. 检查环路:BPF程序必须是有向无环图(DAG),不能有死循环。这意味着你不能写while(1),递归也不行(除非是尾调用)。
  4. 4. 检查寄存器状态:确保你在使用寄存器之前,已经给它赋值了。

常见的验证失败原因翻来覆去就那几样。

访问数据包的时候没判断数据边界,这是新手最容易犯的错,指针一越界直接报错。

使用未初始化的变量,验证器追着寄存器状态跑,没赋值就用肯定逃不过。

还有就是程序太长,老版本内核限制 4096 条指令,现在放宽到 10 万条了,但写太复杂还是容易超。

很多人吐槽验证器太严,明明逻辑没问题就是不让过。我倒是觉得严点好,线上环境跑的东西,安全永远是第一位的。真要是放了个有问题的程序进去,把内核搞崩了,损失可比改代码那点时间大多了。

2.3 BPF 指令集与执行模型

BPF 的指令集是 RISC 风格的。每条指令 64 位定长编码,包含操作码、目标寄存器、源寄存器、偏移量和立即数等字段。

指令类型大概分这几类:

  • • ALU 运算:加减乘除、位运算、移位
  • • 内存操作:加载(LD)、存储(ST)、加载双字(LDDW)
  • • 分支跳转:条件跳转(JEQ、JGT、JSET 等)、无条件跳转(JA)
  • • 函数调用:调用 Helper 函数(CALL)
  • • 返回:退出程序(EXIT)

LLVM 会把你的 C 代码编译成这些指令。你可以用 llvm-objdump -d 反编译 BPF 目标文件,看到具体的字节码。

听起来和普通机器指令差不多,但限制非常多:

它不能随便访问任意内核地址。

它不能随便调用内核函数。

它不能使用无限不可证明的循环。

它的栈大小有限。

它的程序复杂度会受到验证器限制。

这种设计不是为了让开发者爽,是为了让内核放心。

BPF 程序运行在内核态。一旦失控,影响的是整个系统。普通用户态程序段错误,进程死了。BPF 程序如果像内核模块一样完全自由,写坏了就是 kernel panic,线上机器直接G了。

所以 BPF 的表达能力是在安全约束下逐步扩展的。

这也是为什么你会看到很多版本相关的差异。某些内核版本支持 bounded loop,某些版本支持全局变量,某些版本增强了 kfunc,某些版本对 verifier 的能力又强了一点。

2.4 BPF Helper 函数

既然 BPF 程序待在沙箱里,不能像普通内核代码那样随便去调用内核里的任意函数(比如 printk 这种虽然能用,但也得通过特定的封装),那它怎么和外面的世界交互呢?怎么干活呢?

只能通过官方提供的 Helper 函数。Helper 是内核提供给 BPF 程序的能力接口。你可以把它理解成“内核批准过的函数”。不同类型的 BPF 程序,能调用的 Helper 不一样,相当于内核开放出来的能力接口,给你什么权限你才能干什么活。

Helper 函数有上百个,是内核预定义好的、安全的函数接口,BPF 程序可以调用它们来:

  • • 读写 BPF Map
  • • 获取当前进程信息(PID、comm 等)
  • • 读取内核内存(bpf_probe_read)
  • • 操作网络数据包(bpf_skb_store_bytes、bpf_redirect)
  • • 输出调试信息(bpf_printk、bpf_trace_printk)
  • • 操作 perf 事件和环形缓冲区

Helper 函数不是通过正常的函数调用机制调用的。BPF 的 CALL 指令会直接跳转到内核里预编译好的 Helper 函数地址,没有额外的 FFI(外部函数接口)开销。这也是 BPF 性能高的原因之一。

2.5 JIT 即时编译

验证通过后,BPF字节码会被JIT(Just-In-Time)编译器翻译成当前架构(x86_64, ARM64等)的原生机器指令。

BPF 程序有两种执行方式:解释执行和 JIT 编译执行

解释执行就是内核里的 BPF 解释器一条一条读字节码、一条一条执行。有额外开销,所以很慢,但不需要额外配置。

JIT 编译是把 BPF 字节码翻译成宿主机的原生机器指令——x86_64 就生成 x86_64 指令,ARM64 就生成 ARM64 指令。编译后的原生指令直接跑在 CPU 上,性能接近原生内核代码

JIT 编译后的 BPF 程序比解释执行快 10 倍以上。这也是 BPF 能在高性能网络场景里站住脚的原因之一。

大多数生产环境都会开启 JIT。内核配置里有 CONFIG_BPF_JIT 和 CONFIG_HAVE_BPF_JIT 这两个选项。开了之后,BPF 程序加载时会自动走 JIT 路径。

不过 JIT 你写烂了,一样慢——不同 CPU 架构需要维护独立的 JIT 编译器。x86_64 和 ARM64 的支持比较成熟,RISC-V 的还在完善中。

三、BPF核心机制:Maps与数据交互

3.1 BPF Maps:内核态与用户态的数据桥梁

BPF 程序跑在内核态,用户态程序跑在用户态,两边交换数据靠的就是 Maps

Maps 本质上是内核里的共享数据结构。内核态的 BPF 程序可以读写,用户态的程序可以通过系统调用读写。两边不用拷贝大量数据,就靠这个共享的 Map 来传递信息,效率很高。

别小看这个设计。有了 Maps,BPF 程序就不再是孤立的执行单元了,双向通信一通,能玩的花样就多了。

你可以用 Map 存计数器。

可以存黑名单 IP。

可以存进程统计信息。

可以存延迟直方图。

可以存 socket 映射。

可以存 tail call 程序表。

可以把事件从内核送到用户态。

BPF 程序一般不直接 printf 给你看。它通过 Maps 或 Ring Buffer 把数据交出来。用户态程序再读取、展示、聚合、持久化。

比如一个非常简单的计数 Map 可以这样定义:

struct {    __uint(type, BPF_MAP_TYPE_HASH);    __uint(max_entries, 10240);    __type(key, __u32);    __type(value, __u64);} syscall_count SEC(".maps");

BPF 程序里根据 pid 或 syscall id 更新计数。用户态定时读取 Map,就能看到统计结果。

这就是 BPF 的经典模型,内核态做轻量采集和快速决策,用户态做复杂处理和展示

3.2 主流 Maps 类型

BPF 的 Map 类型有几十种,常用的也就那几类,搞懂了足够应付 90% 的场景。

Hash Map 最常见。 适合 key 不连续、需要动态插入删除的场景。比如按 pid 统计,按 IP 统计,按文件 inode 统计。

Array Map 适合 key 是连续整数的场景。它通常预分配,访问速度快。比如按 CPU 编号、按固定状态码、按 syscall id 做索引。

Per-CPU Map 很适合高频计数。 普通共享 Map 多 CPU 同时更新,可能有竞争。Per-CPU Map 给每个 CPU 一份 value,BPF 程序更新本 CPU 的数据。用户态读取时再聚合。这个设计特别适合网络包计数、事件计数、延迟统计。

Ring Buffer 和 Perf Buffer 用来传事件。 以前很多工具用 Perf Buffer。后来 Ring Buffer 更常见。它适合把结构化事件从内核传到用户态,比如进程 exec 事件、文件打开事件、网络连接事件。

LRU Hash 适合缓存类场景。 Map 满了以后可以淘汰旧项。比如你做 IP 统计,但不想无限增长,就可以考虑 LRU。

Stack 和 Queue 适合栈和队列语义。 Program Array 用来做 tail call。 SockMap、SockHash 更偏 socket 重定向和网络处理。

选型时别贪。

能用 Array 就别上 Hash。 高频写就优先考虑 Per-CPU。 事件流就看 Ring Buffer。 需要淘汰再考虑 LRU。

生产环境最怕“我先随便选一个”。随便选,压测时它会回来找你。

3.3 Maps 的底层实现与性能特性

Map 的底层实现是内核里的一组操作函数指针,不同 Map 的性能差异,本质上是底层实现决定的。

数组 Map 最简单,就是一块连续的内存,索引直接算偏移量,跟访问普通数组没区别。没有额外开销,也不会有内存碎片,性能是天花板级别的。只要场景合适,优先选数组总没错。

哈希 Map 底层是散列表,用拉链法解决碰撞。查找的时候先算哈希值,再遍历链表。元素少的时候性能还行,元素多了碰撞概率上升,性能会往下掉。而且哈希 Map 的内存是动态分配的,增删元素会有额外开销。

Per-CPU 类型的性能优势,本质是用空间换时间,避开了缓存一致性开销。每个 CPU 写自己的副本,不会触发其他 CPU 的缓存失效,写操作极快。对于写多读少的统计场景,这是性价比最高的优化方式。唯一的代价就是多占点内存,现在服务器内存都大,这点代价基本可以忽略。

Ring Buffer 的实现也很巧妙。用了双内存映射的技巧,同一块物理内存映射到两个连续的虚拟地址,这样环形缓冲区回绕的时候不用处理边界,直接连续读写就行,省掉了很多判断逻辑。加上本身是无锁设计,多 CPU 并发写的性能也很好。

这种设计避免了数据拷贝,性能极高。

3.4 用户态与内核态的双向通信机制

有了 Map,双向通信就很简单了。

内核态往用户态传数据,分两种情况。少量的统计数据,直接存在普通 Map 里,用户态定期轮询读取就行,实现简单,开销也小。大量的事件数据,比如追踪日志、网络包,就用 Ring Buffer 批量推送,用户态阻塞等待,有数据就处理,实时性更好。

用户态往内核态传数据,基本都是通过 Map。比如防火墙的黑名单规则,用户态更新哈希 Map,内核态每次处理数据包的时候查一下,规则变了立刻生效,不用卸载重加载程序。还可以用数组 Map 存配置开关,用户态改一下数组里的值,内核态就切换工作模式,非常灵活。

高级一点的玩法,用用户态的 Ring Buffer 反向给内核态发数据,不过用得不多。大部分场景下,用户态写普通 Map 就足够了。

这里要提醒大家一下,Map 是共享的,并发读写的时候要注意安全。尤其是普通的哈希 Map,多 CPU 同时写会有锁竞争,别写太频繁。实在高频的话,就换成 Per-CPU 版本,别硬扛。

四、BPF 钩子机制:程序挂载点全解

BPF 程序不能凭空跑,它必须挂载到某个事件上,事件发生了它才执行。这些挂载点叫 "钩子"(Hook) ,不同的钩子对应不同的执行时机,能拿到的上下文也不一样,选对钩子是写 BPF 程序的第一步。

4.1 网络类钩子

网络类钩子是 BPF 的传统强项,从网卡到协议栈再到套接字,每层都有对应的挂载点。

XDP(eXpress Data Path) 是位置最靠前的钩子,是 BPF 在网络领域最猛的一个钩子。它在网卡驱动收包的瞬间触发,数据包还没进入协议栈,你就能处理它。可以做过滤、转发、负载均衡。因为绕过了整个协议栈,延迟极低,做 DDoS 防护、流量清洗、简单的包过滤,用 XDP 最合适。缺点也有,位置太靠前,能拿到的信息少,连完整的协议头解析都得自己写。而且不是所有网卡驱动都支持原生 XDP,不支持的话只能用通用模式,性能会打折扣。

再往上走就是 TC BPF,挂在流量控制子系统的 ingress 和 egress 路径上。这时候数据包已经完成了协议解析,skb 结构也有了,能拿到 IP、端口、协议类型这些完整信息。适合做复杂的流量控制,比如限速、QoS、精细化防火墙规则。比 XDP 灵活,性能稍微差一点,但也比 iptables 强得多。

再往上还有 Socket BPF,挂在单个套接字上,过滤单个 socket 的收发数据。cgroup BPF 可以针对整个 cgroup 做网络控制,容器场景下用得很多,给某个容器单独做网络限制、流量统计,都靠它。

cgroup BPF。挂载到 cgroup 上,控制 cgroup 内进程的网络行为。

4.2 追踪类钩子

追踪类钩子是可观测性的核心,内核和用户态的函数调用、系统事件,都能挂钩子追踪。

kprobe 和 kretprobe 是动态内核探针。kprobe 挂在函数入口,函数一调用就触发;kretprobe 挂在函数返回,函数执行完返回的时候触发。理论上几乎所有内核函数都能挂,非常灵活,想追踪哪个函数直接写名字就行。排查问题的时候特别好用,想知道某个内核函数的调用次数、参数、返回值,挂个 kprobe 立刻就能看到。

缺点是不稳定,内核版本一变,函数名、参数、甚至函数本身可能就没了,程序直接失效。而且 kprobe 是动态插桩,底层靠修改内核指令实现,有一点点额外开销,高频调用的函数挂多了性能会受影响。生产环境长期跑的话,尽量少用 kprobe

uprobe 和 uretprobe 是用户态版本的探针,用法跟 kprobe 差不多,只不过挂的是用户态函数。比如挂 glibc 的 malloc 和 free,追踪内存分配;挂业务程序的某个函数,看调用参数和耗时。最爽的是不用改业务代码,不用重启程序,直接就能挂上去排查问题。我之前排查 Redis 的慢命令,用 uprobe 挂了命令处理函数,十分钟就定位到了大 key 问题,搁以前得改代码加日志重启,折腾大半天。

tracepoint 是内核静态追踪点。内核开发者提前在代码里埋好点,位置和参数都是固定的,不会随便变。稳定性比 kprobe 强太多,内核版本升级也不影响,生产环境优先选 tracepoint。缺点是数量有限,只有官方预埋的那些,不是什么函数都能追踪。

USDT 是用户态静态探针,相当于用户态的 tracepoint。很多成熟软件比如 MySQL、Redis、Nginx 都预埋了 USDT 探针,位置稳定,参数明确。用起来比 uprobe 靠谱,不会因为软件版本升级导致函数偏移变化而失效。有 USDT 的话优先用,没有再考虑 uprobe

4.3 安全与控制类钩子

安全类钩子是近几年才完善的,给 BPF 加强了安全防护的大门。

LSM BPF 是内核 5.7 版本加的功能,LSM 本身是 Linux 的安全模块框架,SELinux、AppArmor 都是基于它做的。现在 BPF 也能挂到 LSM 钩子上了。可以在内核做安全决策的时候插入 BPF 逻辑——比如在文件访问、进程创建、网络连接这些操作执行之前介入,判断是否允许执行。实现了运行时动态的安全策略。

有了 LSM BPF,就能自己写安全策略,动态加载、动态更新。比如禁止进程读取敏感文件,禁止陌生二进制执行,禁止向外连接高危端口。比 SELinux 配置简单,比内核模块安全,做运行时防护特别合适。很多轻量级 HIDS 产品,底层就是用 LSM BPF 做的拦截。

cgroup BPF 除了网络控制,也能做资源审计和安全管控。针对整个 cgroup 做系统调用审计、文件访问监控,容器场景下正好用。一个容器一个 cgroup,策略直接挂上,隔离性也好。现在云原生安全方案里,cgroup BPF 是标配组件。

五、BPF 开发工具链

BPF 工具链已经迭代了好几代,从最早手写汇编,到 bcc 快速开发,再到现在的 libbpf 标准范式,工具越来越好用,门槛也越来越低。

5.1 工具链选型对比

最早的时候,写 BPF 得手写汇编指令,那真不是人干的活,只有内核开发者能玩。后来有了 llvm 支持,可以用 C 写代码,编译成 BPF 字节码,但是加载、Map 操作这些都得自己调系统调用,非常繁琐。

然后是 BCC(BPF Compiler Collection)。把底层的编译、加载、Map 操作全封装好了,用户态用 Python 写,几行代码就能跑起来一个 BPF 程序。一下子把门槛打下来了,很多人都是从 BCC 入门的 BPF。但是 BCC 依赖太重,要装内核头文件、要装 Python、要带 LLVM 运行时,线上环境部署特别麻烦。而且每次运行都要现场编译,启动慢,性能也一般。

再后来官方推出了 libbpf 库,配合 BTF 和 CO-RE 技术,实现了一次编译到处运行(前提是内核支持 BTF)。编译好的 BPF 程序就是个普通的 ELF 文件,用户态用 libbpf 加载,不用依赖内核头文件,不用带编译器,一个二进制就能跑。性能好、依赖少、部署简单,现在已经是官方推荐的标准开发范式。

bpftrace 是另一条线。类 DTrace 的高级脚本语言,一行脚本就能做复杂的追踪。适合快速诊断和临时调试。

还有 ebpf-go(Go 语言生态)和 Aya(Rust 生态)。如果你所在的技术栈是 Go 或 Rust,可以考虑。

选型建议:

  • • 快速临时调试 → bpftrace
  • • 开发生产级监控 → BCC 或 libbpf-tools
  • • 容器化部署 → libbpf-tools(CO-RE 避免每次编译)
  • • Go 技术栈 → ebpf-go

5.2 辅助工具:bpftool、bpf_asm、llvm 工具集

这几个辅助工具,用熟了能省很多事。

bpftool 是内核自带的 BPF 管理工具,功能巨全。查看系统里加载了哪些 BPF 程序,每个程序的类型、挂载点、指令数;查看所有的 Map,Map 的类型、大小、元素数量;加载卸载程序、操作 Map 元素、生成 BTF 文件、生成 skeleton 头文件,啥都能干。调试的时候基本离不开它,有事没事就用 bpftool prog list 看看程序挂没挂上,用 bpftool map dump 看看 Map 里的数据对不对。

llvm 工具集是编译 BPF 程序的基础。clang 负责把 C 代码编译成 BPF 字节码,llvm-objdump 可以反汇编字节码,验证编译结果对不对。写复杂程序的时候,偶尔需要反汇编看看生成的指令,排查问题。

bpf_asm 是 BPF 汇编器,手写汇编的时候用,一般人基本碰不到。除非是做底层优化或者写编译器,不然不用管它。

初学 BPF,我建议准备一台较新的 Linux 测试机或虚拟机。

内核版本别太老,装好 clang、llvm、bpftool、libbpf-dev、make、gcc。

先跑 libbpf-bootstrap 这种样例,再跑 bpftrace one-liner,再看 BCC 工具,不要第一天就写 Cilium,那是自虐。

可以先做三个小练习。

一个 tracepoint 程序,统计 execve。

一个 kprobe 程序,统计 vfs_read。

一个 XDP 程序,丢弃指定 UDP 端口。

这三个练习做完,你会对 BPF 的加载、挂载、Map、Verifier、用户态读取有基本手感。

BPF 学习很吃手感,光看文章不写代码,很容易觉得自己会了。

一写代码,Verifier 立刻告诉你:你不会。

六、BPF 实战:网络领域应用

网络是 BPF 最成熟的应用领域,也是最能体现性能优势的地方。传统 iptables、LVS 这些方案,在大规模场景下性能瓶颈越来越明显,BPF 正好补上了这块短板。

6.1 XDP 高性能数据包处理原理

传统的网络收包路径:网卡 → 中断 → 驱动 → 协议栈(IP、TCP/UDP)→ socket → 用户态。这一路下来,延迟几十到上百微秒

XDP 的路径:网卡 → 驱动 → XDP 钩子。数据包还在驱动层,BPF 程序就能处理了。处理完可以:

  • • XDP_PASS:放行,走正常协议栈
  • • XDP_DROP:丢弃(DDoS 防护场景)
  • • XDP_REDIRECT:重定向到另一个网卡或 CPU
  • • XDP_TX:从同一个网卡发出去

因为绕过了协议栈,延迟从百微秒级降到微秒级

XDP 有三种运行模式,原生驱动模式性能最好,需要网卡驱动支持,现在主流的万兆网卡基本都支持。通用模式是内核模拟的,不用驱动支持,但是性能差不少,适合调试用。还有卸载模式,直接把 BPF 程序卸载到网卡硬件上执行,CPU 完全不参与,性能最强,但是只有少数高端网卡支持。

XDP 程序示例(极简版防火墙):

// xdp_firewall.c#include <linux/bpf.h>#include <bpf/bpf_helpers.h>#include <bpf/bpf_endian.h>#include <linux/if_ether.h>#include <linux/ip.h>// 黑名单 Map:存储要丢弃的源 IPstruct {    __uint(type, BPF_MAP_TYPE_HASH);    __uint(max_entries, 10000);    __type(key, __u32);   // IP 地址    __type(value, __u8);  // 占位} blacklist SEC(".maps");SEC("xdp")int xdp_firewall(struct xdp_md *ctx){    void *data_end = (void *)(long)ctx->data_end;    void *data = (void *)(long)ctx->data;struct ethhdr *eth = data;    if ((void *)(eth + 1) > data_end)        return XDP_PASS;    if (eth->h_proto != bpf_htons(ETH_P_IP))        return XDP_PASS;struct iphdr *ip = (void *)(eth + 1);    if ((void *)(ip + 1) > data_end)        return XDP_PASS;    __u32 src_ip = ip->saddr;    __u8 *val = bpf_map_lookup_elem(&blacklist, &src_ip);    if (val)        return XDP_DROP;  // 黑名单 IP,直接丢    return XDP_PASS;}char LICENSE[] SEC("license") = "GPL";

6.2 基于 TC BPF 的流量控制与监控

XDP 虽然快,但能做的事情有限。需要复杂的流量处理,还是得用 TC BPF。

TC BPF 挂在 Linux 流量控制子系统的 ingress 和 egress 节点上。这时候数据包已经完成了协议解析,有完整的 skb 结构,能拿到 IP 头、TCP 头、端口号所有信息。可以做很精细的流量处理,适合做更复杂的策略。

比如容器网络里,很多策略和转发逻辑需要知道更多元信息。包已经进入内核网络栈,skb 上的信息更丰富。你可以做 ingress 和 egress 方向处理,也能和 qdisc、netdevice、namespace 等机制配合。

很多人纠结 XDP 和 TC 怎么选,其实很简单:只做简单的包过滤、DDoS 防护,追求极致性能,选 XDP。需要复杂的协议处理、流量控制、状态化规则,选 TC BPF。两者也可以搭配用,XDP 做第一层粗过滤,把明显的攻击包丢掉,剩下的交给 TC 做精细化处理。

6.3 负载均衡与服务发现的 BPF 实现

BPF 做四层负载均衡,性能比传统的 LVS 还要好。

传统 LVS 虽然也在内核态,但是路径长,还要做连接跟踪,开销不小。用 BPF 做的话,直接在 TC 或者 XDP 层处理数据包,查后端地址,直接修改包头发出去,路径短很多,性能更高。

而且 BPF 的可编程性强,可以实现更灵活的负载均衡算法、健康检查逻辑,还能跟服务发现结合,动态更新后端列表。不用像 LVS 那样靠用户态工具配置,实时性更好。

比如 Cilium 的服务发现,完全用 BPF 替代了 kube-proxy。K8s 里的 Service 转发,以前靠 kube-proxy 写 iptables 规则,集群规模大了之后,iptables 规则成千上万,匹配性能极差。换成 BPF 之后,用哈希 Map 存服务和后端的映射,查起来 O (1),性能提升非常明显。集群越大,优势越明显

6.4 实战案例:用 BPF 实现简易防火墙与流量统计

假设我们要做一个简单防火墙,需求如下:

用户态把黑名单 IP 写到 Map。

XDP 程序收到包后解析源 IP。

如果源 IP 在黑名单里,丢弃。

Map 定义:

struct {    __uint(type, BPF_MAP_TYPE_HASH);    __uint(max_entries, 65536);    __type(key, __u32);    __type(value, __u8);} blacklist SEC(".maps");

XDP 逻辑:

SEC("xdp")int xdp_firewall(struct xdp_md *ctx){    void *data = (void *)(long)ctx->data;    void *data_end = (void *)(long)ctx->data_end;struct ethhdr *eth = data;    if ((void *)(eth + 1) > data_end)        return XDP_PASS;    if (eth->h_proto != __constant_htons(ETH_P_IP))        return XDP_PASS;struct iphdr *iph = (void *)(eth + 1);    if ((void *)(iph + 1) > data_end)        return XDP_PASS;    __u32 src = iph->saddr;    __u8 *blocked = bpf_map_lookup_elem(&blacklist, &src);    if (blocked)        return XDP_DROP;    return XDP_PASS;}

看起来是不是挺简单?但生产要考虑的东西马上就来了。

IP 字节序怎么处理?

黑名单更新频率多高?

Map 满了怎么办?

IPv6 支不支持?

需要统计命中次数吗?

统计用共享 Map 还是 Per-CPU Map?

程序更新时 Map 状态要不要保留?

误封怎么快速恢复?

这就是 BPF 的真实样子,Demo 很短,工程很长

6.5 Cilium:云原生时代的 BPF 网络方案

Cilium 是 BPF 在云原生领域最成功的项目,把 BPF 在云原生网络的价值打透了。

传统的 K8s 网络方案,依赖 iptables、IPVS 做服务转发,依赖 veth 设备做容器网络,性能损耗大,配置复杂,排障困难。Cilium 完全基于 BPF 重构了容器网络,每个 Pod 的网络流量都由 BPF 程序处理,不用再靠虚拟设备和 iptables。

服务转发直接在内核层用 BPF 哈希表做,性能比 kube-proxy 高好几倍。网络策略也是 BPF 实现的,精细化的端口、协议、身份控制,比 iptables 灵活。还自带可观测能力,网络流量、连接状态、延迟都能直接采集,不用额外装监控工具。

现在越来越多的公司开始把 K8s 集群的网络方案换成 Cilium,尤其是大规模集群,性能提升非常明显。可以说,BPF 能在云原生领域火起来,Cilium 功不可没。

七、BPF 实战:可观测性与性能分析

可观测性是 BPF 应用最广的领域,也是普通开发者接触最多的场景。不用改业务代码,就能深入内核看系统运行状态,这在以前是想都不敢想的。

7.1 CPU 性能剖析:调度、热点函数、上下文切换

传统的 top 或者 pidstat 只能告诉你某个进程占了 80% 的 CPU,但它没法告诉你这 80% 里,进程是在执行底层的哪个函数,是在忙着做哈希运算,还是在死锁等待,精度有限。

有了 BPF 之后,可以做更精准的统计,我们可以写一个高频采样工具。利用 perf_event 钩子,设定每秒钟让内核强行中断 CPU 99 次。每次中断发生时,BPF 程序会顺着当前的 CPU 寄存器指针,把当前正在执行的内核或用户态函数调用栈(Stack Trace)给扒下来,然后存进一个专门的 Stack Map 里。

积少成多,运行个 10 秒钟后,用户态程序把这些调用栈的频次导出,通过 Brendan Gregg 的火焰图工具一渲染。哪些函数是真正的吞金兽,一目了然。

7.2 内存观测:分配、泄漏、页缓存分析

内存问题排查起来一直很头疼:内存泄漏、缓存膨胀、匿名页增长、slab 异常,很多时候不是一眼能看出来。

BPF 可以追踪内存分配相关函数,比如 kmalloc、kfree、malloc、free、mmap、brk 等。

用户态内存可以用 uprobe 挂 malloc/free。

内核态可以看 kmem tracepoint。

比如统计进程 malloc 大小分布。

bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc{    @bytes[comm] = sum(arg0);}'

这只是示意,真实环境里 libc 路径、符号、优化、jemalloc/tcmalloc 都会影响结果。

内存观测最怕只看分配不看释放。

你要建立生命周期视角。分配在哪里发生,释放在哪里发生,中间对象归谁管理。BPF 能帮你抓现场,但不能替你理解业务对象生命周期。

内核内存也一样,slab 增长,要看哪个 cache。

page cache 高,要看是不是正常缓存。

匿名页涨,要看进程映射。

不要一看到内存高就喊泄漏。

很多时候只是 Linux 在用内存做缓存。你要分清楚。

7.3 IO 与文件系统追踪

文件 IO 问题经常伪装成业务慢,磁盘 IO 慢,先得知道是谁在读写,读的什么,延迟多少。

业务说接口慢。

数据库说我没慢。

网络说我也没慢。

最后一看,是某个路径在疯狂写小文件,或者日志刷盘策略有问题。

BPF 可以追踪 vfs_read、vfs_write、openat、fsync、block IO tracepoint。

比如看谁在 fsync。

bpftrace -e 'kprobe:vfs_fsync_range { @[comm] = count(); }'

看块 IO 延迟分布。

bpftrace -e 'tracepoint:block:block_rq_issue { @start[args->sector] = nsecs; }tracepoint:block:block_rq_complete /@start[args->sector]/ {    @lat = hist(nsecs - @start[args->sector]);    delete(@start[args->sector]);}'

这个示意脚本别直接照搬到生产哈,真实场景 key 选 sector 可能不够严谨,设备、请求对象等都要考虑。这里主要是让你看到思路。

BPF 做 IO 分析的关键是把路径串起来。

系统调用层看到 read/write。

VFS 层看到文件系统入口。

块层看到请求下发和完成。

设备层看到实际延迟。

不同层看到的问题不一样,只看一层,很容易误判。

我排查 IO 问题,现在基本先跑个 biolatency 看延迟分布,再跑个 biotop 看进程排行,基本就能定位个八九不离十。这俩都是 BCC 自带的工具,底层就是 BPF,不用自己写。

7.4 网络延迟与丢包定位

网络问题说实话最烦人了,应用说请求慢,客户端说超时,服务端说没收到,抓包一看,又太多。

当延迟高了,丢包了,你不知道是业务问题、内核问题还是网络设备问题。BPF 能帮你在内核网络路径上做定点观测。

你可以看 TCP 重传。

看 connect 延迟。

看 accept 队列。

看 SYN 丢弃。

看 skb drop 原因。

看 qdisc 排队。

看某个 cgroup 或进程的网络行为。

比如统计 TCP 重传。

bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[comm] = count(); }'

看连接建立。

bpftrace -e 'kprobe:tcp_connect { @[comm] = count(); }'

网络观测要注意维度,只按进程统计可能不够,还要按源 IP、目的 IP、端口、namespace、cgroup、网卡、协议状态去切。

但维度越多,Map 压力越大,事件量越大。

所以还是那句话:内核里少做,抓关键证据,别把 BPF 程序写成数据仓库

7.5 实战案例:用 bpftrace 快速定位系统瓶颈

假设线上机器突然抖动,CPU 不算高,但接口延迟飙。

你可以按这个顺序粗查。

看调度延迟。

runqlat

看磁盘延迟。

biolatency

看 TCP 连接生命周期。

tcplife

看文件打开。

opensnoop

看进程执行。

execsnoop

这些很多都来自 BCC 工具集或类似工具,如果没有现成工具,再用 bpftrace 补。

比如看某个 tracepoint 是否频繁触发。

bpftrace -e 'tracepoint:sched:sched_process_exec { printf("%s\n", comm); }'

排查时别一上来就写复杂脚本,先用粗粒度工具定位方向,然后缩小范围,最后再写定制 BPF 程序。

这跟看病一样。先问诊,再检查,最后才手术。不能上来就开刀。

7.6 BCC 工具集:常用观测工具速查

BCC 自带了上百个现成的观测工具,大部分场景直接拿来用就行,不用自己写。列几个最常用的,遇到对应问题直接掏出来。

  • • execsnoop:监控新启动的进程。谁偷偷起了进程,是不是有恶意脚本,一抓一个准。
  • • opensnoop:监控文件打开操作。哪个进程打开了什么文件,权限够不够,都能看到。
  • • tcpretrans:实时看 TCP 重传,哪些 IP、哪些端口在重传,一目了然。
  • • biotop:磁盘 IO 排行,看哪个进程读写磁盘最多。
  • • runqlat:调度延迟统计,看进程等待 CPU 的时间,排查系统负载高但 CPU 使用率低的问题。
  • • offcputime:统计进程阻塞时间,以及阻塞的调用栈。性能上不去,CPU 又不高,大概率是阻塞太多,用这个工具一查就知道在哪阻塞。
  • • memleak:内存泄漏检测,跟踪分配释放,找出泄漏点。不用重启程序,线上直接跑。
  • • biolatency:看块 IO 延迟。
  • • biosnoop:看具体 IO 请求。
  • • tcplife:看 TCP 连接生命周期。
  • • tcpconnect:看主动连接。
  • • tcpaccept:看被动连接。
  • • profile:做采样分析。

这些工具不一定每台机器都装,但你应该知道它们能干什么。

因为很多时候,排查效率来自“知道有这个工具”。

八、BPF 实战:安全审计与防护

安全是 BPF 近几年崛起的新赛道。传统安全方案要么太重,要么侵入性强,BPF 轻量、高效、无侵入的特点,正好契合了运行时安全的需求。

8.1 LSM BPF 运行时安全防护

LSM BPF 最大的优势,是能做主动拦截,而不只是事后审计

传统的审计方案,都是事情发生了才记录,最多发个告警,伤害已经造成了。LSM BPF 是在操作执行之前介入,判断不合法就直接拒绝,把攻击拦在发生之前。

比如进程要读取 /etc/shadow 文件,LSM 钩子会在打开文件之前触发 BPF 程序,程序判断进程是否有权限,没有就直接返回错误,文件根本打不开。再比如进程要执行一个未知的二进制文件,直接拦截,防止恶意程序运行。

相比 SELinux,LSM BPF 的策略是可编程的,灵活度高。可以写复杂的判断逻辑,可以动态加载卸载,可以跟外部安全系统联动,实时更新策略。配置门槛也低很多,不用写复杂的策略文件,用 C 写 BPF 程序就行,开发者更容易上手。

当然了,它也不是要替代 SELinux。两者定位不一样,SELinux 是系统级的强制访问控制,LSM BPF 更适合做定制化的、动态的安全策略。两者结合用,效果最好。

8.2 进程行为审计与异常检测

进程是安全事件的核心载体,把进程的全生命周期监控起来,大部分攻击行为都能发现。

用 BPF 可以追踪所有的 fork、exec、exit 事件。每个进程什么时候创建的,父进程是谁,执行了什么命令,什么时候退出,全链路记录。形成完整的进程树,攻击者就算隐藏进程,也逃不过内核层面的追踪。

异常检测就是在这个基础上做的,比如正常业务进程不会起 bash,不会执行 nc、curl 这些工具,一旦检测到就告警。再比如进程突然创建大量子进程,大概率是在挖矿或者爆破。

传统的审计方案靠 auditd,性能差,配置麻烦,信息也不全。BPF 做的审计,直接在内核钩子上取数据,性能开销小,信息更全,还不会被用户态的恶意程序绕过。毕竟内核层面拿到的数据,比用户态靠谱多了。

8.3 文件访问与系统调用监控

敏感文件的访问是安全监控的重点,配置文件、密码文件、密钥文件,这些文件被非法访问,后果会很严重。

用 BPF 监控 openat 等系统调用,或者直接用 LSM 钩子,记录所有访问敏感文件的进程、操作类型、时间。谁读了、谁改了,都有完整日志。一旦有非预期的访问,立刻告警。

系统调用监控更底层。每个进程调用了哪些系统调用,参数是什么,都能记录。恶意程序干坏事,总得调用系统调用吧。比如调用 ptrace 注入其他进程,调用 mmap 分配可执行内存,这些都是危险信号。BPF 可以做细粒度的系统调用审计,甚至拦截危险的系统调用。

当然了,全量监控系统调用开销很大,一般只监控危险的几十种,够用就行。真要全量监控,高并发场景下性能还是扛不住。

8.4 网络连接与恶意流量检测

网络是攻击者的必经之路,外联控制、数据窃取、横向移动,都得走网络。

网络安全检测不能只看包,还要看进程,用 BPF 可以监控所有的网络连接创建。每个进程什么时候连了什么 IP、什么端口,是入站还是出站,全记录下来。跟威胁情报库比对,一旦连接恶意 IP 立刻告警。

恶意流量检测也可以做,比如端口扫描,短时间内大量连接不同端口,直接识别出来,用 XDP 直接拉黑源 IP。还有 DDoS 攻击,流量特征匹配上了就直接在网卡层丢弃,不用进协议栈。

相比传统的 IDS/IPS,BPF 做的检测性能更高,部署更简单,不用串接在网络里,单机就能实现。适合做主机侧的流量防护,跟边界防护形成互补。

8.5 实战案例:基于 BPF 的轻量级入侵检测

说个基于 BPF 的轻量级入侵检测简单的思路,用 BPF 做一个轻量级 HIDS,核心就三个监控点。

第一是进程执行监控。用 tracepoint 的 sys_enter_execve 钩子,记录所有 execve 调用,拿到命令行、进程 PID、父 PID。跟内置的黑名单比对,比如 bash 反向连接、nc、挖矿程序特征,命中就告警。

第二是敏感文件访问。用 LSM 的 file_open 钩子,监控 /etc/shadow、/root/.ssh/id_rsa 这些敏感路径,非白名单进程访问就告警。

第三是可疑外联。跟踪 connect 系统调用,记录对外连接。监控高危端口,比如 22、3389 的外联,还有陌生境外 IP 的连接,发现异常就告警。

这三点加起来,就能覆盖大部分常见的入侵行为。而且都是内核层面的监控,不会被攻击者轻易绕过,性能开销也很小,CPU 占用基本在 1% 以内,完全可以线上长期跑。

不用装重型的安全客户端,就一个小二进制,部署简单,资源占用低。很多创业公司的服务器安全,其实这么一套就够用了。

内核态代码 exec_monitor.bpf.c

#include <linux/bpf.h>#include <bpf/bpf_helpers.h>#include <bpf/bpf_tracing.h>// 传给用户态的事件结构体struct exec_event {    __u32 pid;    __u32 ppid;    char comm[16];    char filename[256];};// Ring Buffer,用户态读事件用struct {    __uint(type, BPF_MAP_TYPE_RINGBUF);    __uint(max_entries, 256 * 1024);} rb SEC(".maps");SEC("tracepoint/syscalls/sys_enter_execve")int trace_execve(struct trace_event_raw_sys_enter *ctx){struct exec_event *e;    __u64 id = bpf_get_current_pid_tgid();    __u32 pid = id >> 32;    // 从Ring Buffer里申请一块内存    e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);    if (!e)        return 0;    e->pid = pid;    e->ppid = bpf_get_current_ppid();    bpf_get_current_comm(&e->comm, sizeof(e->comm));    // 文件名是用户态指针,绝对不能直接解引用,必须用probe_read_str安全读取    bpf_probe_read_user_str(&e->filename, sizeof(e->filename), (void *)ctx->args[0]);    // 提交事件,用户态立刻就能收到    bpf_ringbuf_submit(e, 0);    return 0;}char _license[] SEC("license") = "GPL";

编译加载:

# 编译字节码clang -O2 -target bpf -c exec_monitor.bpf.c -o exec_monitor.bpf.o# 加载程序并pin到bpffsbpftool prog load exec_monitor.bpf.o /sys/fs/bpf/exec_monitor type tracepoint# 挂载到对应的tracepoint上bpftool tracepoint attach name syscalls:sys_enter_execve prog pinned /sys/fs/bpf/exec_monitor

想看实时事件直接用 bpftool 读 Ring Buffer:

bpftool map dump name rb -f

只要系统里有新进程启动,立刻就能抓到 PID、父 PID、进程名和执行的文件路径。想做告警的话,写个简单的用户态程序读 Ring Buffer,匹配一下 bash 反向连接、命令这些特征,命中就发告警,比装个笨重的安全客户端轻量多了。

踩坑提醒: 用户态传进来的指针,不管看起来多靠谱,都不能直接解引用。我见过不少新手图省事直接拷贝字符串,轻则验证器报错,重则直接把内核搞 panic。还有 Ring Buffer 别设太小,服务器上批量执行脚本的时候事件会突然涌进来,缓冲区小了容易丢事件,256KB 是比较稳妥的大小。

九、BPF 进阶技术

想写更复杂的 BPF 程序,就得了解一些进阶特性,这些特性能帮你写出更高效、更灵活的代码,解决更复杂的问题。

9.1 尾调用(Tail Calls)与程序链式执行

BPF 程序有指令数限制,早年是 4096 条,现在虽然放宽到 10 万条,但特别复杂的逻辑还是装不下,尾调用就是解决这个问题的

尾调用就是一个 BPF 程序跳转到另一个 BPF 程序,直接替换当前的执行上下文,没有函数返回,也不会增加栈深度。就像函数尾调用优化一样,跳过去就不回来了。

有了尾调用,就可以把复杂逻辑拆成多个小程序,按流程链式调用。比如网络协议解析,第一层解析以太网头,第二层解析 IP 头,第三层解析 TCP 头,每层一个小程序,用尾调用串起来。逻辑清晰,还能避开指令数限制。

还可以用来实现状态机,不同状态对应不同的程序,根据当前状态跳转到对应的处理逻辑。灵活度很高。

尾调用的数量限制是 32 次。超过了就拒绝执行,调用的时候需要用程序数组 Map 存目标程序的文件描述符,提前加载好。而且尾调用不会返回原程序,跳转之后就直接在新程序里执行到结束。

// 尾调用示例(简化)bpf_tail_call(ctx, &prog_array, index);// 调用后不会返回,直接执行 prog_array[index] 指向的程序

9.2 全局变量、Per-CPU 变量与原子操作

早年的 BPF 程序没有全局变量,所有共享数据都得放 Map 里,用起来麻烦,性能也差。内核 5.1 之后支持了全局变量,写起来就方便多了。

全局变量直接在 C 代码里定义,跟普通 C 程序一样。底层其实还是存在 Map 里,但是编译器和 libbpf 帮你处理了,不用自己手动操作 Map。存个配置开关、统计计数器啥的,比自己定义 Map 省事。

Per-CPU 变量跟 Per-CPU Map 一个道理,每个 CPU 一份副本。写的时候不用加锁,性能好。适合做每个 CPU 的统计计数,最后用户态汇总。比全局变量加锁的方式性能高很多,高并发场景优先用。

原子操作跟普通 C 里的一样,__sync_fetch_and_add 这类原子内置函数都支持。全局变量多 CPU 并发写的时候,要用原子操作,不然会有竞态。当然了,最好的方式还是用 Per-CPU 变量,从根源上避免竞争,比原子操作性能还好。

9.3 BPF 程序类型全解与适用场景

BPF 程序类型有二十多种,每种对应不同的挂载点,上下文不一样,能调用的 Helper 也不一样。选对类型很重要。场景不同,权限不同。

主要的程序类型:

类型
挂载点
用途
BPF_PROG_TYPE_SOCKET_FILTER
Socket
包过滤
BPF_PROG_TYPE_KPROBE
kprobe
内核追踪
BPF_PROG_TYPE_TRACEPOINT
tracepoint
静态追踪
BPF_PROG_TYPE_XDP
XDP
高速包处理
BPF_PROG_TYPE_PERF_EVENT
perf_event
性能采样
BPF_PROG_TYPE_CGROUP_SKB
cgroup
cgroup 网络策略
BPF_PROG_TYPE_LSM
LSM
安全策略
BPF_PROG_TYPE_SK_LOOKUP
socket 查找
负载均衡

9.4 内核版本兼容性是 BPF 绕不开的坑

BPF 迭代特别快,每个内核版本都会加新功能、新 Map、新 Helper。所以生产部署前要做能力探测。

大概梳理一下关键节点:

3.18 版本正式引入 eBPF 和 bpf 系统调用,基础框架成型。4.x 系列是快速发展期,4.1 加 kprobe 和 TC 支持,4.7 加 XDP 和 tracepoint,4.15 加 cgroup BPF。这阶段功能逐步完善,但是很多高级特性没有,兼容性也一般。

5.x 系列进入成熟期。5.0 之后加了很多核心特性,5.3 支持有界循环,5.4 是第一个功能比较完善的 LTS 版本,大部分基础功能都有了。5.7 加入 LSM BPF,安全领域的大门打开。5.8 加入 Ring Buffer,解决了 Perf Buffer 的痛点。5.10 是现在最常用的 LTS 版本,CO-RE 支持良好,功能也全,生产环境用得最多

6.x 系列继续扩展功能,加了更多 Helper 和 Map 类型,优化了验证器,支持更多架构。如果是新业务,能上 6.x LTS 的话体验更好。

生产环境选型,尽量选 LTS 版本。5.4 是底线,再老的版本很多功能用不了,bug 也多。能上 5.10 最好,常用特性都有,稳定性也经过了验证。

写程序的时候如果要兼容低版本内核,就得注意用的特性和 Helper 是不是对应版本都有。必要的时候做特性检测,运行的时候判断内核版本,用不同的逻辑。

9.5 BPF 并发模型与锁机制

BPF 程序可能在各种上下文运行,中断上下文、软中断上下文、进程上下文,都有可能。并发安全是必须考虑的问题。

大部分场景下,优先用 Per-CPU 数据结构。每个 CPU 自己玩自己的,不共享,自然就没有并发问题。这是性能最高的方案,能不用锁就不用锁。

如果必须共享数据,比如全局状态,那可以用自旋锁。BPF 提供了 bpf_spin_lock,放在 Map 元素里,用 bpf_spin_lock 和 bpf_spin_unlock 操作。注意不是所有程序类型都能用自旋锁,比如 kprobe、tracepoint 这些追踪类的就不行,因为验证器没法检查抢占安全。网络类、LSM 类的程序一般可以用。

用锁的时候一定要注意,持锁时间越短越好。BPF 里不能做休眠,锁要是被长时间持有,会影响性能,甚至死锁。只在操作共享数据的时候加锁,操作完立刻释放。

还有个原则,能不用锁就不用。很多时候换个数据结构,比如用 Per-CPU Map,就能避开锁的问题,性能还好,何必自找麻烦。

十、性能优化

BPF 虽然好用,但写不好一样有性能问题,一样会出故障。说说生产环境踩过的坑,以及优化的思路。

10.1 BPF 程序的性能开销评估方法

BPF 开销到底大不大?

看场景。

一个低频 tracepoint 程序,几乎感觉不到。

一个每个网络包都执行的 XDP 程序,任何多余操作都会被放大。

一个每次系统调用都触发的 tracing 程序,如果还输出事件到用户态,很快就能把系统拖慢。

所以不要凭感觉,要压测。模拟线上的流量或者事件频率,跑 BPF 程序,看 CPU、内存占用多少。用 perf top 看 BPF 相关函数的占比,大概就能知道开销有多大。

网络类的程序,主要看软中断 CPU 使用率。XDP 和 TC BPF 基本都跑在软中断里,软中断占比高不高,有没有打满核心,是关键指标。

追踪类的程序,开销跟触发频率强相关。比如追踪每个数据包,高流量下开销肯定大;追踪每秒几次的系统调用,开销就可以忽略。所以一定要评估事件触发频率,频率太高的话要么做采样,要么加过滤,别全量抓。

一般来说,设计合理的 BPF 程序,CPU 开销控制在 5% 以内是正常的。超过 10% 就要考虑优化了,除非是特别重的场景。

10.2 常见性能瓶颈与优化手段

BPF 常见性能问题翻来覆去就那几个:

Map 查找太多。 每个包或每次事件做多次 Hash Map lookup,成本会累积。能合并就合并。能用 Array 就别用 Hash。能用 Per-CPU 就别共享写。

事件上报太频繁。 ring buffer 不是黑洞。用户态消费不过来就会丢。高频事件必须过滤、采样、聚合。

解析逻辑太复杂。 网络包解析层层嵌套,边界检查多,分支多,指令数就上去了。协议支持越多,越要考虑拆分和 fast path。

共享状态竞争。 多个 CPU 写同一个 Map value,原子操作或锁都会带来开销。计数类优先 Per-CPU。

用户态处理太慢。 BPF 只是前半段。用户态 agent 如果消费慢、序列化慢、写后端慢,照样出问题。

还有一个很常见的小坑。

调试输出别留在线上。bpf_printk 很方便,但别在高频路径乱用。调试时用一下可以,生产热路径里疯狂 printk,那是真会出事。

10.3 Verifier 报错排查要学会读日志

BPF 加载失败时,Verifier log 是救命的

不要只看一句 permission denied 或 invalid argument

要把 verifier log 打开,看它到底在哪条指令拒绝。

常见错误包括:

未初始化寄存器。

R0 未设置返回值。

Map lookup 后未判空。

指针越界。

访问 ctx 偏移不合法。

循环无法证明有界。

栈越界。

Helper 参数类型错误。

不同分支下指针状态不一致。

排查技巧如下:

把复杂代码拆小。

减少宏嵌套。

先让最小程序加载成功。

逐步加逻辑。

对包解析每一步都做边界检查。

对 Map 返回值立刻判空。

对变量范围做显式判断。

必要时看 xlated 指令。

有时候 C 代码看起来没问题,但 clang 优化后生成的 BPF 指令让 verifier 不好证明。你稍微改一下写法,它就过了。

有时候就是很玄学的,但更多是 verifier 的静态分析能力有限。你要写它能理解的代码,跟它硬刚没用。

10.4 内存安全、边界检查与常见陷阱

陷阱 1:指针访问越界。必须每次访问前检查 ptr + size <= data_end

陷阱 2:栈溢出。BPF 栈只有 512 字节。别在栈上放太大的结构体。

陷阱 3:未初始化寄存器。验证器会检查,但有时候编译器优化会让它误判。

陷阱 4:Helper 函数参数类型错误。仔细看 Helper 函数的文档,参数类型错了验证器会拒。

陷阱 5:无限循环。验证器要求循环有界。用 #pragma unroll 展开循环,或者用固定次数的 for 循环。

10.5 生产环境部署的稳定性建议

BPF 程序上线,要有退路。

能卸载。 能禁用。 能降级。 能观测自身状态。 能限制事件量。 能处理 Map 满。 能兼容不同内核。 能在加载失败时不影响主业务。

网络类程序尤其要谨慎。

XDP 程序一旦写错,可能直接丢业务流量。上线前要在测试环境压测。灰度时只挂一部分机器或一部分网卡。关键路径要有旁路方案。策略更新要能回滚。

可观测性程序也要控制范围。

不要默认全量开启所有探针。 不要默认采集所有进程。 不要默认输出所有事件。

安全类程序要有紧急开关。

策略系统一定要支持白名单和回滚。告警模式和拦截模式要分开。先审计,再拦截。别一上来就拦。

还有权限问题。

加载 BPF 程序需要相关能力。不同发行版和内核配置对 unprivileged BPF 的限制也不同。生产 agent 通常需要较高权限,这本身就是安全风险。你要保护好这个 agent,不然它会变成新的攻击面。

工程上还有对象生命周期。

程序 attach 了没有? Map pin 到 bpffs 了吗? 进程退出后对象还在不在? 更新时旧程序有没有 detach? Map 状态是否保留? 异常退出会不会残留对象?

这些问题都很碎。

但线上事故往往就藏在这些碎地方。

写了这么多,其实也只是 BPF 技术的冰山一角。这几年 BPF 发展太快了,每年都有新特性出来,新的玩法被发明出来。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-03 04:00:32 HTTP/2.0 GET : https://f.mffb.com.cn/a/500301.html
  2. 运行时间 : 0.236096s [ 吞吐率:4.24req/s ] 内存消耗:4,900.41kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=02cccaaf8b93aa0df77c7d86012a358f
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000654s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000625s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.040113s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.001955s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000603s ]
  6. SELECT * FROM `set` [ RunTime:0.000690s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000611s ]
  8. SELECT * FROM `article` WHERE `id` = 500301 LIMIT 1 [ RunTime:0.000791s ]
  9. UPDATE `article` SET `lasttime` = 1783022432 WHERE `id` = 500301 [ RunTime:0.044018s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000349s ]
  11. SELECT * FROM `article` WHERE `id` < 500301 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000543s ]
  12. SELECT * FROM `article` WHERE `id` > 500301 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000722s ]
  13. SELECT * FROM `article` WHERE `id` < 500301 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001189s ]
  14. SELECT * FROM `article` WHERE `id` < 500301 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.008078s ]
  15. SELECT * FROM `article` WHERE `id` < 500301 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.006938s ]
0.237667s