近两年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. 你和你的好友,正在免费帮微信训练神经网络
  2. pom.xml配置详解
  3. NB-IoT与LoraWan技术分析与前景展望
  4. php53 php55区别,详解 PHP 中的三大经典模式
  5. checkbox 最多选两项
  6. TCP程序流程及服务器客户端
  7. 小a与星际探索---DP
  8. php array 数组函数,php array数组函数
  9. 【GitHub】Linux 内核揭秘:linux-insides-zh
  10. Restify Api 开发经验
  11. 实用技巧:快速定位Zuul的性能瓶颈
  12. 云桌面优缺点_桌面云优缺点
  13. Office Open XML 文档格式(转)
  14. 积分器-微分器-抽取器
  15. matlab改进平方根算法,改进平方根请教
  16. python计算样本方差_Python numpy 样本方差估计
  17. 微软因果推理的框架DoWhy github 介绍
  18. 新疆旅游攻略-喀纳斯
  19. 软件测试的未来:2021年需要关注的15大软件测试趋势
  20. 手绘风格的白板Excalidraw

热门文章

  1. UI动效设计工具都有哪些?
  2. Chapter 08:轮廓和形状检测
  3. crontab每小时执行
  4. pytorch安装本地安装
  5. 项目管理SPI,CPI,CV,SV
  6. linux centos 如何查看操作系统版本信息?
  7. 基于STM32按键的防抖和松开处理:状态机
  8. AI: 2021 年人工智能前沿科技报告02(更新中……)daiding
  9. Java:iText生成pdf文档
  10. Android车载应用开发与分析(6)- 车载多媒体(一)- 音视频基础知识与MediaPlayer