本文整理自”Linux内核之旅开源社区“直播教学视频《BPF C编程入门》,主讲人为Linux内核之旅社区研三贺东升,喜欢看视频的小伙伴可以通过文章末尾的视频链接观看。
主要内容包括:
bcc是BPF高度封装的框架,我们使用的时候可以直接运行脚本,这对于使用上来说是非常友好的,但对于我们理解背后的原理来说是不清晰的,因为bcc的高度封装性,把背后的一些编译的过程都给屏蔽掉了,不利于我们去理解整个BPF机制的工作原理。使用c编程的完整的过程,更贴近于eBPF的机制流程。这是我们今天选择BPF C作为编程入门介绍的原因。
简单回顾一下上次介绍的eBPF机制的框架图:
如图所示,左边是BPF工具,使用BPF工具需要我们自己来编写BPF程序,BPF程序经过Clang和LLVM会编译生成一段BPF字节码,通过用户空间的加载器将BPF字节码,通过系统调用的方式加载进内核。
进入到内核的流程:首先验证器verifier进行一些循环的检查,还有一些安全的验证,验证通过以后就可以交由BPF虚拟机执行。eBPF机制有许多的hook点,而这些hook点是事件触发类型。在函数上挂好这些钩子以后,当函数被调用的时候,就会触发这些点,进而执行我们的BPF程序。
如果我们想把数据存在一个地方的话,可以通过BPF的map。在BPF程序中创建map,把我们想获取的内核数据保存在map中。在用户态调用一些接口来读或写map,把保存的数据取出来。
eBPF可以帮助我们做什么?
简单来说我们可以通过在用户空间写BPF程序,拿取内核中的一些数据。例如内核中某个内核函数的调用次数或者某个内核函数的执行时间。在用户态编写好BPF程序以后把它加载到内核,就可以拿到我们想要的内核数据。
BPF C编程环境搭建
基本环境:
环境搭建步骤:
1.下载内核源码
下载的内核源码与Ubuntu 18.04的内核版本一致。首先查看当前内核版本:uname -r。
root@ubuntu:~# uname -r
5.4.0-65-generic
然后在内核源码镜像站点(http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/)
下载对应版本的内核源码包,解压在/usr/src目录下。
root@ubuntu:/usr/src# ls
linux-5.4
2.安装依赖项
root@ubuntu:/usr/src/linux-5.4# apt install libncurses5-dev flex bison libelf-dev binutils-dev libssl-dev
3.安装Clang和LLVM
root@ubuntu:/usr/src/linux-5.4# apt install clang llvm
4.配置源码
在源码根目录下使用make defconfig生成 .config文件。
root@ubuntu:/usr/src/linux-5.4# make defconfig
5.解决modpost:not found错误
因为直接make M=samples/bpf时,会报缺少modules的错误。修复modpost的报错,以下两种解决方案二选一:
方案一(修复模块)
root@ubuntu:/usr/src/linux-5.4# make modules_prepare
方案二(补全脚本)
root@ubuntu:/usr/src/linux-5.4# make scripts
6.关联内核头文件
root@ubuntu:/usr/src/linux-5.4# make headers_install
7.编译内核BP**F程序样例**
在源代码目录下执行make M=samples/bpf。
root@ubuntu:/usr/src/linux-5.4# make M=samples/bpf
此时进入/linux-5.4/samples/bpf/中,会看到生成了BPF字节码文件*_kern.o和用户态的可执行文件。
下一步我们就可以编写我们自己的BPF程序了。整个的流程如下图:
使用BPF C编写运行一个hello world程序
1.编写hello_kern.c
root@ubuntu:/usr/src/linux-5.4/samples/bpf# vim hello_kern.c
#include <uapi/linux/bpf.h>
#include"bpf_helpers.h"
SEC("kprobe/sys_write")
int bpf_prog(void *ctx)
{
char msg[]="hello world!\n";
bpf_trace_printk(msg,sizeof(msg));
return 0;
}
char _license[] SEC("license")="GPL";
//注:编写BPF C程序要进入linxu-5.4/samples/bpf目录进行编辑
2.编写hello_user.c
root@ubuntu:/usr/src/linux-5.4/samples/bpf# vim hello_user.c
#include "BPF_load.h"
int main(void)
{
if (load_BPF_file("hello_kern.o"))
return -1;
read_trace_pipe();
return 0;
}
3.**修改Makefile,添加新增需要编译的文件**
root@ubuntu:/usr/src/linux-5.4/samples/bpf# vim Makefile
# List of programs to build
hostprogs -y := hello
# LibBPF dependencies
hello-objs := BPF_load.o hello_user.o
#Tell kbuild to always build the programs
always += hello_kern.o
//tips:所有添加项直接添加在原有内容的末尾即可。
4.返回源码根目录make
root@ubuntu:/usr/src/linux-5.4/samples/bpf# cd ../..
root@ubuntu:/usr/src/linux-5.4/samples/bpf# make M=samples/BPF
5.运行用户态加载器
root@ubuntu:/usr/src/linux-5.4/samples/bpf# ./hello
1.BPF 程序的节
看到在上一次的内容中我们提到了一个概念叫做BPF程序的节。SEC宏会把名字为括号中的字符串编译到elf的目标文件中。如图:
节本身是elf文件中的概念。SEC宏中第一个为程序类型,第二个是我们要跟踪的函数。对于SEC宏来说的话他会把整个kprobe/sys_write当成节的名字,编译到elf的目标文件中。
通过readelf工具查看目标文件中得节头信息:
root@ubuntu:/usr/src/linux-5.4/samples/bpf# readelf -S hello_kern.o
可以看出,SEC宏的作用是把名字为括号中的字符串编译到elf的目标文件中。
2.BPF 程序的字节码
使用objdump查看BPF程序的字节码:
root@ubuntu:/usr/src/linux-5.4/samples/bpf# objdump -s hello_kern.o
可以看到kern.o它是目标文件,格式为elf64,采用小端来存储。这里面就显示了我们节的一些信息。比如说我们用SEC定义的节名字叫做kprobe/sys_bpf,还有个licence许可证的节,说明我们用SEC宏把我们后面定义的一些名字编译到elf目标文件中的某个节中。现在我们应该理解了SEC宏的作用。
在宏的下面是BPF程序,它保存在名字为kprobe/sys_write的节中的。
右边图中橙色框圈起来的就是BPF字节码,是左面灰色部分圈起来的代码经过clang和llvm来生成的BPF的字节码。这段字节码可以被虚拟机解码执行。
接下来我们介绍一下BPF**内核辅助函数调用是怎么转化为BPF**字节码的。
首先我们来使用llvm-objdump工具来反汇编BPF字节码来看一下他的BPF汇编表示。
可以看到它的里面有文件格式是ELF64-BPF,第二行是我们用SEC宏定义的节的名字kprobe/sys_bpf,bpf_prog是我们定义的BPF程序的名字。
重点关注第10行,为汇编表示在汇编里面调用函数函数它是地址,但是我们可以看到6它并不是实际的地址,而是整形数,左面对应是汇编的BPF字节码。
下面我们就来进一步分析BPF内核辅助函数调用是怎么被编译成BPF字节码。
从头文件入手
/tools/testing/selftests/bpf/bpf_helpers.h(line 25)
/include/uapi/linux/bpf.h(line 2754)
我们首先来从两个头文件入手,也是我们最开始写BPF程序的时候两个必须包含的头文件。其中在bpf_helpers.h里面可以看到图示一段定义,函数指针的变量明正是我们在BPF程序里面调用的内核辅助函数的名字。在文件中是以函数指针的形式来定义的,我们可以看函数指针它是用以强制类型转换赋给了指针变量,我们在BPF程序中实际调用的并不是真正的内核辅助的函数接口,而是函数指针。
BPF_FUNC_map_lookup_elem()函数是在文件uapi/bpf.h文件中定义的,它不是直接定义,而是通过两个宏来定义的。
宏展开过程
bpf_call 123是伪汇编的表示,并不是真正的BPF指令,它对应的真正的BPF指令为BPF_EMIT_CALL(FUNC)。
可以看到BPF_EMIT_CALL BPF指令宏调用了内核辅助函BPF_FUNC_map_lookup_elem()。
我们就可以理解 FUNC 其实就是BPF辅助函数的id号。
我们前面那两个宏展开以后得到的枚举类型。说func 谁对应的枚举类型里面的某个整型值,替换一下后,最下面的一句就更容易我们理解。
我们用BPF指令宏的时候
接下来 因为我们在bpf程序中调用的是trace_printk函数,我们还是以trace_printk来进行举例。
继续将指令宏展开,会得到bpf_insn结构体,即为BPF指令集的格式。
其中,BPF_CALL为真正的BPF调用指令,dst_reg为目的寄存器,sec_reg为源寄存器,off为偏移量,imm为立即数。
进行替换后得到如图示结构体,code表示操作码,在内核中分别定义为
#define BPF_JMP 0x05
#define BPF_CALL 0x80
按位与后得到code的值为85,目的寄存器、源寄存器和off均被初始化为0,imm为枚举结构中的整型值,即为6。
进行组合
bpf_insn表示的BPF指令级的格式,我们把里面的成员按照字节序的方式来组装一下就可以得到我们对应BPF字节码。 我们进行组装的时候一定要按照BPF指令集定义的形式来组装。
查看JIT前后BPF字节码的变化
观察JIT前后的变化:objdump&&bpftool
objdump -s hello_kern.o //显示不直观
llvm-objdump -d hello_kern.o //显示直观
./bpftool prog show //显示加载的BPF程序
./bpftool prog dump xlated id xx //xlated模式将BPF指令翻译为汇编指令打印出来
./bpftool prog dump xlated id xx opcodes //使用opcodes修饰符可在输出中包含BPF指令的opcode
我们写的BPF程序首先通过clang它是llvm的前端,它的作用来最后会生成ll IR文件,传给llvm的后端。llvm的后端可以支持BPF类型,会生成BPF类型的字节码。生成BPF字节码以后,通过用户态的加载器通过系统调用进入到内核。
现在我们就把整个编译过程来理解一下
首先来说一下clang和llvm的关系。
先说一下llvm是什么?我们说最后要用llvm来编译 但是并不是说l lv m 它是编译器它本身并不是编译器 它只是可以说它是提供了一些开发编译器还有些解释器 些语言工具的一些库。同时ll v m我们可以理解成样那种框架 框架里面它其实还包含那些工具链的组件比如说l lc 也我们llvm的后端,或者说一些其他的工具。
这幅图就展示了它的编译过程
clang的作用 用做词法分析、语法分析还有语义分析,最关键的一步它会生成
IR文件 ,IR是llvm的中间表示 它也是一种语言,类似于底层那种汇编语言。生成IR以后会把IR文件交给llvm的后端,在后端的时候生成对应平台的机器码。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/3AGOiJJDD7Z7U01rtYV0ZA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。