BPF 的全称是 Berkeley Packet Filter,是一个用于过滤(filter)网络报文(packet)的架构。(例如tcpdump),目前称为Cbpf(Classical bpf)
eBPF全称 extended BPF,Linux Kernel 3.15 中引入的全新设计, 是对既有BPF架构进行了全面扩展,一方面,支持了更多领域的应用,比如:内核追踪(Kernel Tracing)、应用性能调优/监控、流控(Traffic Control)等;另一方面,在接口的设计以及易用性上,也有了较大的改进。
eBPF 支持在用户态将 C 语言编写的一小段“内核代码”注入到内核中运行,注入时要先用 llvm 编译得到使用 BPF 指令集的 ELF 文件,然后从 ELF 文件中解析出可以注入内核的部分,最后用 bpf_load_program() 方法完成注入。 用户态程序和注入到内核中的程序通过共用一个位于内核的 eBPF MAP 实现通信。为了防止注入的代码导致内核崩溃,eBPF 会对注入的代码进行严格检查,拒绝不合格的代码的注入。
| 优劣 | eBPF | 源码开发 | 热补丁 |
|---|---|---|---|
| 优势 | 1.安全,不会引起宕机 2.自主,可控 2.热加载(良好的加载/卸载流程) 3.开启CO-RE后,移植性高,适配量小 4.可以在注入的代码中写入业务逻辑,优化hids性能 5.开发难度低,上手快 |
1.体积小 2.自由度高 3.性能高 4.功能强大 |
1.体积小 2.自由度高 3.性能高 4.热加载,不需要重启 |
| 缺点 | 1.功能受限(验证器) 2.强依赖于内核版本 3.不支持内核函数调用 4.单函数最大512byte栈空间,通过尾调用扩展到8K 5.性能不如其他两者 |
1.需要重新编译内核 2.需要重启业务主机 3.需要开发者熟悉内核 4.适配工作量巨大 5.netlink上发数据有性能瓶颈 |
1.需要开发者熟悉内核 2.适配工作量大 |



用 C 编写 BPF 程序
用 LLVM 将 C 程序编译成对象文件(ELF)
用户空间 BPF ELF 加载器(例如 libbpf)解析对象文件
加载器通过 bpf() 系统调用将解析后的对象文件注入内核
内核验证 BPF 指令,然后对其执行即时编译(JIT),返回程序的一个新文件描述符
利用文件描述符 attach 到内核子系统(例如网络子系统)
某些子系统还支持将 BPF 程序 offload 到硬件(例如网卡)。
| bcc | libbpf | ebpfgo | cilium eBPF | |
|---|---|---|---|---|
| https://github.com/iovisor/bcc | https://github.com/libbpf/libbpf | https://github.com/aquasecurity/libbpfgo | https://github.com/cilium/ebpf | |
| 优势 | 1.开发活跃 2.示例多 |
1.linux官方提供,可靠 2.支持CO-RE |
1.Go库,符合技术栈 2.支持CO-RE |
1.纯Go库,大厂背书 2.开发活跃 3.部分支持CO-RE |
| 缺点 | 1.需要在目标机器编译,对业务影响大 |
1.前端语言为C |
1.需要开启CGO |
1.对CO-RE支持的不全面 |
当eBPF被用来做信息收集功能时,就得和内核中各种结构体打交道,众所周知,linux内核改动一向比较随(keng)意(die),不会像windows那样还考虑兼容性,所以我们得自己解决不同内核版本直接字段不一致问题。
常规内核代码写法是通过宏定义来判断内核版本,在编译的时候走不同的代码分支,解决差异性,方法虽然不难,但是适配却非常费劲,当需要支持的内核版本多时,光是适配就得耗费大量精力。
static __always_inline u32 get_task_ns_pid(struct task_struct *task)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
// kernel 4.14-4.18:
return task-> [PIDTYPE_PID].pid->numbers[task->nsproxy->pid_ns_for_children->level].nr;
#else
// kernel 4.19 onwards:
return task->thread_pid->numbers[task->nsproxy->pid_ns_for_children->level].nr;
#endif
}
这也就是BTF出现之前的很长一段时间里, bcc + clang + llvm 被人们诟病的地方,程序在运行的时候,才进行编译,目标机器还得安装clang llvm kernel-header头文件,同时编译也会消耗大量cpu资源,这在某些高负载机器上是不能被接受的。
因此BTF & CO-RE横空出现,BTF可以理解为一种debug符号描述方式,此前传统方式debug信息会非常巨大,linux内核一般会关闭debug符号,btf的出现解决了这一问题,大幅度减少debug信息的大小,使得生产场景内核携带debug信息成为可能。
CO-RE正是基于这一技术开发的,原理类似于pe/elf结构中的重定位表,核心思想就是采用非硬编码形式对成员在结构中的偏移位置进行描述,解决不同版本间结构体差异性。
可喜的是通过运用这项技术,确实可以帮助开发者节省大量精力在版本适配上,但是这项技术目前还是在开发中,还有许多处理不了的场景,比如结构体成员被迁入子结构体中,这时候还是需要手动解决问题,BTF的开发者也写了一篇文章,讲解不同场景的处理方案 bpf-core-reference-guide
tips:目前cilium提供的eBPF库对CO-RE的支持也不全面,等待社区持续更新。
考虑到自身业务技术栈,因此选用cilium提供的go库作为前端库,同时默认开启btf,增强程序可移植性。
Mac + Vscode(安装remote develop插件) 强烈推荐
ubuntu 20.10 server(5.8之后开启BTF的内核都可以)
建议安装最新5.16内核版本且开启BTF,不喜欢折腾就直接安装ubuntu20.10 server版本,默认开启了BTF
无特殊要求,引入 github.com/cilium/ebpf 库即可。
uname -r | grep BTF 其中CONFIG_DEBUF_INFO_BTF开启即可,未开启则需要重新编译内核,开启BTF。
#include "vmlinux.h" //linux内核头文件大集合
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
//包含这些头文件,就可以用CORE编程了
(这里没啥好说的,就和写内核代码一样,只是注意能用的函数比较少,同时如果遇到编译问题,请参考笔者踩坑记录【eBPF开发记录】)
方法一:手动编写,自主可控,实现
TARGETS := kern/sec_socket_connect
TARGETS += kern/tcp_set_state
TARGETS += kern/dns_lookup
TARGETS += kern/udp_lookup
# Generate file name-scheme based on TARGETS
KERN_SOURCES = ${TARGETS:=_kern.c}
KERN_OBJECTS = ${KERN_SOURCES:.c=.o}
LLC ?= llc
CLANG ?= clang
EXTRA_CFLAGS ?= -O2 -emit-llvm -g
linuxhdrs ?= /lib/modules/`uname -r`/build
LINUXINCLUDE = \
-I$(linuxhdrs)/arch/x86/include \
-I$(linuxhdrs)/arch/x86/include/generated \
-I$(linuxhdrs)/include \
-I$(linuxhdrs)/arch/x86/include/uapi \
-I$(linuxhdrs)/arch/x86/include/generated/uapi \
-I$(linuxhdrs)/include/uapi \
-I$(linuxhdrs)/include/generated/uapi \
-I/usr/include \
-I/home/cfc4n/download/linux-5.11.0/tools/lib
all: $(KERN_OBJECTS) build
@echo $(shell date)
.PHONY: clean
clean:
rm -rf kern/*.o
rm -rf user/bytecode/*.o
rm -rf network-monitoring
$(KERN_OBJECTS): %.o: %.c
$(CLANG) $(EXTRA_CFLAGS) \
$(LINUXINCLUDE) \
-include kern/chim_helpers.h \
-Wno-deprecated-declarations \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-pragma-once-outside-header \
-Wno-address-of-packed-member \
-Wno-unknown-warning-option \
-fno-unwind-tables \
-fno-asynchronous-unwind-tables \
-Wno-unused-value -Wno-pointer-sign -fno-stack-protector \
-c $< -o -|$(LLC) -march=bpf -filetype=obj -o $(subst kern/,user/bytecode/,$@)
build:
go build .
方法二:采用cilium提供的bpf2go库
all:
go generate
go build
clean:
-rm *_bpfe*.o
-rm *_bpfe*.go
-rm eBPF-*

以下信息来自笔者查看Linux Kernel Release文档总结得出 Kernel Release Note
4.7 支持tracepoint
4.16 且 LLVM 6.0 不再使用宏always_inline 修饰函数,支持bpf程序调用非bpf程序
4.18 支持btf jit支持32位cpu
5.1 Add __sk_buff->sk, struct bpf_tcp_sock, BPF_FUNC_sk_fullsock and BPF_FUNC_tcp_sock | 增强btf能力 | 指令数量从4096提高到100w条
5.2 支持全局变量
5.3 支持有限for循环
5.5 Add probe_read_user, probe_read_kernel and probe_read_user_str, probe_read_kernel_str | 支持 BPF_CORE_READ
5.7 加入bpf-lsm框架 (selinux appamor)
5.8 加入CAP_BPF and CAP_PERFMON | 引入Ring buffer
5.10 支持尾调用(long jump)和普通函数调用(func call)混用
总结:内核组能支持的越新越好,如果能支持Ring buffer那就能解决数据乱序问题,且传输性能优于Perf Buffer。


总结:
共同点:
/proc/sys/net/core/bpf_jit_harden 设置为 1 会为非特权用户( unprivileged users)的 JIT 编译做一些额外的加固工作。比如常量致盲,损失部分性能。
/proc/sys/kernel/unprivileged_bpf_disabled 设置为1会禁止非特权用户使用 bpf(2) 系统调用,将它设为 1,就没有办法再改为 0 了,除非重启内核。一旦设置为 1 之后,只有初始命名空间中有 CAP_SYS_ADMIN 特权的进程才可以调用 bpf(2) 系统调用 。 Cilium 启动后也会将这个配置项设为 1
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_CLS_BPF=m
CONFIG_NET_CLS_ACT=y
CONFIG_BPF_JIT=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_TEST_BPF=m
本文由博客一文多发平台 OpenWrite 发布!
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它: