近两年BPF技术跃然成为了一项热门技术,在KubeCon 2020 Europe会议上有7个关于BPF的技术分享, 而在KubeCon 2020 China会议上也已有了3个关于BPF技术的中文分享,分别来自腾讯和PingCAP,涉足网络优化和系统追踪等领域。在中文社区里,包括阿里巴巴、网易、字节跳动等国内第一梯队IT公司也越来越关注BPF这项新技术。

一、前言

作为一个coder,时不时会遇到性能问题,有时候明明看资源,cpu,io都占用不高,程序的性能就是上不去,真有一种想进入到计算机里面看看到底发生什么的冲突;还有优化性能的时候不知道整个系统的短板到底是哪一块,如何去优化它?

根本原因其实是对系统的内核不够了解,导致虽然有解决问题的激情和动力,但是总是难找到关键点,彷徨而不得其门。让我学习内核,却又望而退步,觉得难度还是太大,有没有不用深入了解系统内核,但是又能深入观察内核行为的办法那,这时候我发现了BPF和eBPF,通过它有了透视内核的能力,所以就开始了BPF学习之旅。

二、BPF是个什么

BPF原来是Berkely Packet Filter(伯克利数据包过滤器)的缩写,原来是提升pcap过滤性能的,比当时最快的包过滤技术快20倍,只所以性能高,是因为它工作在内核中,避免包从内核态复制到用户态所以速度快,后来Alexei Starovoitov 大牛在2014年重新实现了BPF,将其扩展成了通用的执行引擎,称为eBPF,官方缩写仍是BPF。

简单解释BPF作用,BPF提供了一种当内核或应用特定事件发生时候,执行一段代码的能力。BPF 采用了虚拟机指令规范,所以也可以看一种虚拟机实现,使我们可以在不修改内核源码和重新编译的情况下,提供一种扩展内核的能力的方法。

三、BPF能干嘛

BPF程序不像一般程序可以独立运行,它是被动运行的,需要事件触发才能运行,有点类似js里面的监听,监听到按钮点击执行一小段代码。这些事件包括系统调用,内核跟踪,内核函数,用户函数,网络事件等。

具体能干嘛那,作用还是很强大,可以进行系统故障诊断,因为其有透视内核的能力;网络性能优化,因为它可以在内核态接收网络包,并做修改和转发;系统安全,因为它可以中断非法连接等;性能监控,因为其透视能力,可以查看函数耗费时间从而我们可以知道问题到底出在哪里。 如下图:

四、BPF如何工作

经典的BPF的工作模式是用户使用BPF虚拟机的指令集定义过滤表达式,传递给内核,由解释器运行,使得包过滤器可以直接在内核态工作,避免向用户态复制数据,从而提升性能,比如tcpdump的BPF过滤指令实例如下:

[root@localhost ~]# tcpdump -d port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2 jf 10
(002) ldb      [20]
(003) jeq      #0x84            jt 6 jf 4
(004) jeq      #0x6             jt 6 jf 5
(005) jeq      #0x11            jt 6 jf 23
(006) ldh      [54]
(007) jeq      #0x50            jt 22 jf 8
(008) ldh      [56]
(009) jeq      #0x50            jt 22 jf 23
(010) jeq      #0x800           jt 11 jf 23
(011) ldb      [23]
(012) jeq      #0x84            jt 15 jf 13
(013) jeq      #0x6             jt 15 jf 14
(014) jeq      #0x11            jt 15 jf 23
(015) ldh      [20]
(016) jset     #0x1fff          jt 23 jf 17
(017) ldxb     4*([14]&0xf)
(018) ldh      [x + 14]
(019) jeq      #0x50            jt 22 jf 20
(020) ldh      [x + 16]
(021) jeq      #0x50            jt 22 jf 23
(022) ret      #262144
(023) ret      #0
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.

执行过程如下:

后来又一位大牛EricDumazet在2011年7月发布的Linux 3.0中增加了JIT(即时编译),性能比解释执行更快,多像java的虚拟机,可以解释执行也可以即时编译执行。

现在BPF的执行过程如下示意图:

  • 编写eBPF 代码;

  • 将eBPF代码通过LLVM把编写的eBPF代码转成字节码;

  • 通过bpf系统调用提交给系统内核;

  • 内核通过验证器对代码做安全性验证(包括对无界循环的检查);

  • 只有校验通过的字节码才会提交到JIT进行编译成可以直接执行的机器指令;

  • 当事件发生时候,调用这些指令执行,将结果保存到map中;

  • 用户程序通过映射来获取执行结果。

五、BPF 和内核模块对比

BPF程序会进行安全检查,内核模块可能会引入Bug。

BPF程序不能随意调用内核函数,只能调用部分辅助函数。

BPF的栈空间最大为512个字节,不能扩大,只能借助map存储。

BPF程序可以一次编译到处运行,因为它依赖的辅助函数,映射表,BPF指令集属于稳定的API。

六、编写BPF程序

6.1 准备知识

开发BPF指令显然不适合直接用BPF指令开发,所以大牛们开发了一些前端工具让我们可以更方便的开发,比如我们可以通过C来编写BPF程序,然后通过LLVM编译成BPF。

当然还是负载,又有了BCC和bpftrace。BCC即BPF Compiler Collection,提供了开发BPF跟踪程序的高级框架,提供编写内核BPF程序的C语言环境,同时提供了许多高级语言的接口,比如pyhton等。同时BCC中提供了很多BPF工具,让我们可以方便使用用于性能分析和故障分析,在开发BPF程序之前可以看看。

bpftrace编写单行程序或短小脚本更加适合,BCC适合编写复杂的脚本和作为后台进程使用。libbcc和libbpf为两者提供底层支持。

BPF程序编写可以借助工具

BCC开发的动态追踪工具集

6.2 环境准备

我的测试环境是centos8.5版本,内核版本为4.18,而BPF最好用5.x版本的内核需要先升级下。

[root@localhost ~]# cat /etc/centos-release
CentOS Linux release 8.5.2111
[root@localhost ~]# uname -a
Linux localhost.localdomain 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
1.2.3.4.

内核升级步骤:

#1. 到[https://www.kernel.org/](https://www.kernel.org/)查看稳定的内核版本为5.16.10
#2. 下载编译
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.16.10.tar.xz
tar xvf linux-5.16.10.tar.xz
cd linux-5.16.10/
uname -a
cp /boot/config-4.18.0-348.7.1.el8_5.x86_64  .config
#注释掉CONFIG_SYSTEM_TRUSTED_KEYS
make  menuconfig
#进入界面按tab 选择Load 加载.config ,在Save后即可用原来配置编译#编译内核核心
make -j 4
make modules_install
#安装内核核心
make install
grub2-set-default 0   #0表示 /boot/grub2/grub.cfg 文件中排在第一位的 menuentry 段
reboot
make modules_prepare
make script
make headers_install   INSTALL_HDR_PATH=/usr/include
#安装bpf 实例
make M=samples/bpf
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

安装BPF相关库和工具:

yum install libbpf-devel make clang llvm elfutils-libelf-devel bpftool bcc-tools bcc-devel
1.
  • llvm : 将eBPF程序编译成字节码工具。

  • c代码构建工具make。

  • eBPF工具集BCC和它依赖的头文件。

  • libelf库以及ebpf管理工具ebpftool。

  • 用户程序通过BPF映射查询到BPF字节码的字节码运行结果。

6.3 依赖BCC开发BPF的helloworld

步骤如下:

用C语言开发一个eBPF程序;

用LLVM把eBPF程序编译成BPF字节码;

通过bpf系统调用,把BPF字节码提交给内核;

内核验证并运行BPF字节码,并把相应状态保存到BPF映射中;

用户程序通过 BPF 映射查询 BPF 字节码,得到执行结果;

这个流程一般比较麻烦,可以利用BCC来简化,用python脚本加载BPF程序,编译为字节码,并通过系统调用将BPF字节码,运行BPF字节码;

6.3.1 用C开发一个eBPF程序

int hello(void *ctx)
{bpf_trace_printk("Hello, World!");return 0;
}
1.2.3.4.5.

bpf_trace_printk 是常用的BPF辅助函数,它就是简单的打印一个字符串;不过eBPF输出是内核调试文件:

/sys/kernel/debug/tracing/trace_pipe
1.

6.3.2 使用python和BCC开发BPF的加载程序

#!/usr/bin/env python3
# 1) 导入BCC库中的BPF模块
from bcc import BPF# 2) 加载C程序开发的BPF程序
b = BPF(src_file="hello.c")
# 3) 将此BPF程序挂载到内核探针,其中do_sys_openat2是系统调用openat 在内核实现
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) 读取和打印 /sys/kernel/debug/tracing/trace_pipe
b.trace_print()
1.2.3.4.5.6.7.8.9.10.

运行查看:

> python3 hello.pyb'       pmdalinux-1298    [007] d..31  6758.674383: bpf_trace_printk: Hello, World!'
b'       pmdalinux-1298    [007] d..31  6758.674395: bpf_trace_printk: Hello, World!'
b'       pmdalinux-1298    [007] d..31  6758.674410: bpf_trace_printk: Hello, World!'
b'       pmdalinux-1298    [007] d..31  6758.674422: bpf_trace_printk: Hello, World!'
b'       pmdalinux-1298    [007] d..31  6758.674426: bpf_trace_printk: Hello, World!'
b'       python3-73326   [001] d..31  6758.674859: bpf_trace_printk: Hello, World!'
b'      irqbalance-942     [006] d..31  6758.894331: bpf_trace_printk: Hello, World!'
b'      irqbalance-942     [006] d..31  6758.894593: bpf_trace_printk: Hello, World!'
1.2.3.4.5.6.7.8.9.10.

问题解决

问题一:编译过程磁盘空间满了

按照[https://blog.csdn.net/xionglangs/article/details/108866146]扩展磁盘;(https://blog.csdn.net/xionglangs/article/details/108866146)
1.

问题二:make -j4 编译报错

BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
Failed to generate BTF for vmlinux
Try to disable CONFIG_DEBUG_INFO_BTF
make: *** [Makefile:1106: vmlinux] Error 1
1.2.3.4.

解决办法: 注释掉.config中的CONFIG_DEBUG_INFO_BTF 或 yum install dwarves

问题三:编译需要支持bpf

编译内核的时候bpf的编译选项打开,在.config文件中添加或修改

编译内核的时候bpf的编译选项打开,在.config文件中添加或修改
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
1.2.3.4.5.6.7.8.9.10.11.12.

问题四:make M=samples/bpf报错

/root/core/linux-5.16.10/samples/bpf/bpftool//bootstrap/libbpf//include/bpf/bpf_helper_defs.h:322:63: error: unknown type name '__u32'
static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12;^
/root/core/linux-5.16.10/samples/bpf/bpftool//bootstrap/libbpf//include/bpf/bpf_helper_defs.h:350:58: error: unknown type name '__u32'
static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13;^
fatal error: too many errors emitted, stopping now [-ferror-limit=]1. make M=samples/bpf报错
/root/core/linux-5.16.10/samples/bpf/bpftool//bootstrap/libbpf//include/bpf/bpf_helper_defs.h:322:63: error: unknown type name '__u32'
static long (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12;^
/root/core/linux-5.16.10/samples/bpf/bpftool//bootstrap/libbpf//include/bpf/bpf_helper_defs.h:350:58: error: unknown type name '__u32'
static long (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13;^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
1.2.3.4.5.6.7.8.9.10.11.12.13.14.

解决办法:

vim /root/core/linux-5.16.10/samples/bpf/bpftool//bootstrap/libbpf//include/bpf/bpf_helper_defs.h
添加头文件:
#include
#include
1.2.3.4.

问题五:failed to load BTF from /root/core/linux-5.16.10/vmlinux: No such file or directory

Error: failed to load BTF from /root/core/linux-5.16.10/vmlinux: No such file or directory
make[2]: *** [Makefile:179:/root/core/linux-5.16.10/samples/bpf/bpftool/vmlinux.h] 错误 2
make[1]: *** [samples/bpf/Makefile:296:/root/core/linux-5.16.10/samples/bpf/bpftool/bpftool] 错误 2
make: *** [Makefile:1846:samples/bpf] 错误 2
[root@localhost linux-5.16.10]#
1.2.3.4.5.

更改.config 配置:

CONFIG_DEBUG_INFO_BTF=y
make -j4
1.2.

问题六:fatal error: ‘gnu/stubs-32.h’ file not found

make[2]: ***
[Makefile:179:/root/core/linux-5.16.10/samples/bpf/bpftool/vmlinux.h] 错误 2
make[1]: ***
[samples/bpf/Makefile:296:/root/core/linux-5.16.10/samples/bpf/bpftool/bpftool]
错误 2
make: *** [Makefile:1846:samples/bpf] 错误 2

非常神奇的Linux技术:BPF相关推荐

  1. 首本深入讲解Linux内核观测技术BPF的书上市!

    新书速递 导读:BPF通过一种软件定义的方式,将内核的行为和数据暴露给用户空间,开发者可以通过在用户空间编写BPF程序,加载到内核空间执行,进而实现对内核行为的灵活管理和控制. 在计算机系统中,包过滤 ...

  2. 世界级Linux技术大师首次公开大量技术内幕

    媒体评论 "这是我读过的最全面的Linux设备驱动程序著作." --Alan Cox,Linux内核维护者 "这本书涵盖了各种Linux设备驱动程序,全面而翔实.&quo ...

  3. Web技术栈中不可或缺的Linux技术

    随着第三次信息浪潮的冲击,web技术在近年来可谓发生了天翻地覆的变革.从单向信息的web1.0时代,逐步过渡到信息和人交互的web2.0再到数据主动与人发生关系的web3.0时代,这些成就无疑归功于W ...

  4. Linux技术研究-基础篇(raid与LVM,配额)

    Linux技术研究-基础篇(raid与LVM,配额) 创建RAID-5 若想建立新的md1设备 只在/dev下建立还不够 重启后会消失 固化的方法是 为了使udev自动产生/dev/md1, /dev ...

  5. BTC:简单易懂比特币之比特币的神奇——区块链技术的体现

    BTC:简单易懂比特币之比特币的神奇--区块链技术的体现 目录 BTC的七大特殊之处 BTC的七大特殊之处 1.一个没有CEO的公司,管理几十万员工: 2.每个员工自私自利,争权夺利,公司运作9年风生 ...

  6. Alibaba Cloud Linux 技术图谱首发,分享学习感言得大奖!

    简介:作为开发者,你对Linux的了解有多少呢?到底要学到什么程度,才能被判定是"精通"呢?如果小白想要入手Linux,该从哪里学起呢? 这时你需要一条有效的学习路径,能囊括详细的 ...

  7. 小女生的Linux技术~~~Linux常识~~21-30

    小女生的Linux技术~~~Linux常识~~21-30 Q21 如何查看当前用户的系统行为? A: 使用命令w查看当前用户的系统行为, w root Q22 如何查看曾经登录系统的用户名 ? A:使 ...

  8. Linux技术网站中文,Linux技术网站,putty工具,中文显示设置

    专业的Linux技术网站,用户遍布全国各地,拥有大批的Linux专家与工程师,汇集海量Linux信息,是中国Linux人的网上家园. 默认情况下,putty是不支持中文显示的,当使用putty ssh ...

  9. Linux技术研究-基础篇(启动和自动挂载)

    Linux技术研究-基础篇(启动和自动挂载) 系统启动流程 如果有一天你的服务器启动不了,面对屏幕上的各种各样的提示素手无策. 你不知道服务器出了什么问题,无法判断启动到了哪个环节. 若想排查出问题原 ...

最新文章

  1. linux 隐藏权限,Linux权限位,s权限,t权限,及隐藏权限
  2. Go gin获取GET请求参数
  3. android 连接 asp.net webservice 简单记录
  4. 加速IE的Javascript的方法
  5. SAP UI5 dialog style max-height
  6. 转:OAUTH协议简介
  7. yolo人脸检测数据集_自定义数据集上的Yolo-V5对象检测
  8. leetcode No.141 环形链表
  9. 应用HttpWebRequest接收post或get数据
  10. leetcode题解167-两数之和 II - 输入有序数组
  11. python方法测试怀孕,Python unittest模拟:是否可以在测试时模拟方法的默认参数的值?...
  12. Android学习笔记之AndroidManifest.xml文件解析(摘自皮狼的博客)
  13. SpringBoot❤SpringClould常用注解史诗级汇总
  14. 《交互设计沉思录》译序
  15. VM虚拟机装Windows XP系统
  16. eureka相同服务名注册多个不同服务
  17. Discom BKS03/KS91D传感器
  18. 无法启动此程序,因为计算机中丢失api-ms-win-crt-runtime-l1-1-0.dll。尝试重新安装该程序以解决此问题。
  19. 12306火车余票查询
  20. ENVI5.3 安装教程,新手入门(超详细)附安装包和常见问题

热门文章

  1. 一个端口只能被一个镜像组镜像 trunk口 交换机各个端口类型的概述 H3C 多个目的镜像端口 多监控口
  2. Color Hunt 漂亮炫酷的配色小程序
  3. SQLServer 资源池没有足够的系统内存来运行此查询
  4. Pytorch 安装
  5. 黑马上新Spring全套教程(含实战源码)
  6. vue配置项目启动时自动打开浏览器
  7. Linux(CentOS)查看系统版本
  8. 管理类联考可以用计算机吗,管理类联考
  9. 《Natural Language Processing with Python》读书笔记 002期
  10. 蓝色车牌识别OpenCV-Python