eBPF学习笔记(一)—— eBPF介绍内核编译

news2024/12/24 8:43:40

eBPF学习笔记(一)—— eBPF介绍&内核编译

  • eBPF介绍
  • eBPF基础
    • 代码验证
    • 即时编译
  • eBPF开发工具
    • BCC
    • bpftrace
    • libbpf C/C++库
    • eBPF Go库
  • 内核编译
    • 查看内核版本
    • 下载内核源码
    • 安装依赖项
    • 安装最新版llvm和clang
    • 配置内核
    • 编译内核BPF示例程序
    • 常见问题
      • 问题一:libbpf: map 'rx_cnt': unexpected def kind var.
      • 问题二:/bin/sh: 1: scripts/mod/modpost: not found
  • 第一个eBPF程序
    • hello_kern.c
    • hello_user.c
    • Makefile
    • 编译
    • 运行
  • 参考资料

eBPF介绍

BPF(Berkeley Packet Filter,拓展的伯克利报文过滤器)是类Unix系统上数据链路层的一种原始接口,提供原始链路层封包的收发。除此之外,如果网卡驱动支持混杂模式,那么它可以让网卡处于此种模式,这样可以收到网络上的所有包,不管他们的目的地是不是所在主机,目前被称为cBPF(classical BPF)

从3.18版本开始,Linux 内核提供了一种扩展的BPF虚拟机,名为“extended BPF”,简称为eBPF。它能够被用于非网络相关的功能,比如附在不同的tracepoints上,从而获取当前内核运行的许多信息。

eBPF现在主要被应用于网络、跟踪、内核优化、硬件建模等领域。
在这里插入图片描述

eBPF基础

eBPF 程序是事件驱动的,能将代码加载进内核中的eBPF虚拟机,并在内核或应用程序通过某个挂钩点时运行。 预定义的钩子包括系统调用、函数进入/退出、内核跟踪点、网络事件等。
在这里插入图片描述
如果不存在用于特定需求的预定义挂钩,则可以创建内核探针 (kprobe) 或用户探针 (uprobe) 以在内核或用户应用程序的几乎任何位置附加 eBPF 程序。
在这里插入图片描述
当程序加载到 Linux 内核中时,它在附加到请求的钩子之前要经过两个步骤:

  1. 代码验证
  2. 即时编译(Just In Time,JIT)

代码验证

验证步骤确保 eBPF 程序可以安全运行。 它验证程序是否满足几个条件,例如:

  1. 加载 eBPF 程序的进程拥有所需的能力(特权)。
    除非启用非特权 eBPF,否则只有特权进程才能加载 eBPF 程序。
  2. 该程序不会崩溃或以其他方式损害系统。
  3. 程序总是运行到完成(即程序不会永远处于循环中,阻止进一步的处理)。

即时编译

即时 (JIT) 编译步骤将程序的通用字节码转换为机器特定的指令集,以优化程序的执行速度,这使得 eBPF 程序可以像本地编译的内核代码或作为内核模块加载的代码一样高效地运行。

eBPF开发工具

目前eBPF存在几个开发工具链来协助 eBPF 程序的开发和管理,用于满足用户的不同需求:

  1. BCC
  2. bpftrace
  3. libbpf C/C++库
  4. eBPF Go库

BCC

BCC 是一个框架,使用户能够编写带有嵌入其中的 eBPF 程序的 python 程序。 该框架主要针对涉及应用程序和系统分析/跟踪的用例,其中 eBPF 程序用于收集统计信息或生成事件,而用户空间中的对应物收集数据并以人类可读的形式显示。 运行 python 程序将生成 eBPF 字节码并将其加载到内核中。
在这里插入图片描述

bpftrace

bpftrace 是 Linux eBPF 的高级跟踪语言,可用于最新的 Linux 内核。 bpftrace 使用 LLVM 作为后端将脚本编译为 eBPF 字节码,并利用 BCC 与 Linux eBPF 子系统以及现有的 Linux 跟踪功能进行交互:内核动态跟踪 (kprobes)、用户级动态跟踪 (uprobes) 和跟踪点。bpftrace 语言的灵感来自 awk、C 和前身跟踪器,例如 DTrace 和 SystemTap。
在这里插入图片描述

libbpf C/C++库

libbpf 库是一个基于 C/C++ 的通用 eBPF 库,它有助于将由 clang/LLVM 编译器生成的 eBPF 目标文件加载到内核中,并且通常通过提供易于使用的库 API 来抽象与 BPF 系统调用的交互应用程序。
在这里插入图片描述

eBPF Go库

eBPF Go 库提供了一个通用的 eBPF 库,它将获取 eBPF 字节码的过程与 eBPF 程序的加载和管理分离。 eBPF 程序通常是通过编写高级语言创建的,然后使用 clang/LLVM 编译器编译为 eBPF 字节码。
在这里插入图片描述

内核编译

本篇将介绍如何编译高版本Linux内核源码中的eBPF示例代码并用其编写自定义例程。

查看内核版本

root@ubuntu:~# uname -r
5.15.0-52-generic

下载内核源码

点此下载与当前系统内核版本一致的内核源码,并解压到/usr/src目录中:

root@ubuntu:/usr/src# ls
linux-5.15

安装依赖项

apt install libncurses5-dev flex bison libelf-dev binutils-dev libssl-dev libcap-dev

安装最新版llvm和clang

1)备份源文件

cp /etc/apt/sources.list /etc/apt/sources.list.bak

2)编辑/etc/apt/sources.list,在末尾加入以下内容,保存

deb http://apt.llvm.org/focal/ llvm-toolchain-focal main
deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal main

deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main
deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main

deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main
deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main

3)更新源

apt update

4)安装llvm

apt install llvm

5)安装clang

apt install clang

更新源的时候可能会出现这个错误:
在这里插入图片描述使用以下命令解决:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 15CF4D18AF4F7421

配置内核

1)进入内核源码目录,后续操作都在源码根目录进行:

cd linux-5.15

2)拷贝vmlinux至当前目录

cp /sys/kernel/btf/vmlinux ./vmlinux

3)在内核源码根目录下生成.config文件

make defconfig

编译内核BPF示例程序

1)关联内核头文件

make headers_install

2)编译

make M=samples/bpf

如果出现以下警告,不影响编译:
在这里插入图片描述

常见问题

问题一:libbpf: map ‘rx_cnt’: unexpected def kind var.

在这里插入图片描述
解决方案:升级llvm和clang至最新版即可

问题二:/bin/sh: 1: scripts/mod/modpost: not found

解决方案

make modules_prepare

第一个eBPF程序

文件目录:/usr/src/linux-5.15/samples/bpf
注意:自Linux内核5.11版本开始,移除了bpf_load.h等头文件,部分函数和宏不再适用,因此在代码的写法上也有不同,网上的demo大多是基于5.10版本以下的,不适用于新版内核,本篇在这里提供新版本写法

hello_kern.c

#include <bpf/bpf_helpers.h>

#define SEC(NAME) __attribute__((section(NAME), used))

SEC("tracepoint/syscalls/sys_enter_execve")		//触发的事件类型/系统调用
int bpf_prog(void *ctx)							//触发事件时调用的函数
{
	char msg[] = "Hello BPF World!";
	bpf_trace_printk(msg, sizeof(msg));			//将信息写入管道,等待用户态读取
	
	return 0;
}

char _license[] SEC("license") = "GPL";

hello_user.c

#include <stdio.h>
#include "trace_helpers.h"

int main(int ac, char **argv)
{
	struct bpf_link *link = NULL;
	struct bpf_program *prog;
	struct bpf_object *obj;
	char filename[256];

	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);	//只有当运行的程序名为hello时才能读到hello_kern.o
	obj = bpf_object__open_file(filename, NULL);
	if (libbpf_get_error(obj)) {
		fprintf(stderr, "ERROR: opening BPF object file failed\n");
		return 0;
	}

	prog = bpf_object__find_program_by_name(obj, "bpf_prog");
	if (!prog) {
		fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
		goto cleanup;
	}

	/* load BPF program */
	if (bpf_object__load(obj)) {
		fprintf(stderr, "ERROR: loading BPF object file failed\n");
		goto cleanup;
	}
    
    
	link = bpf_program__attach(prog);
	if (libbpf_get_error(link)) {
		fprintf(stderr, "ERROR: bpf_program__attach failed\n");
		link = NULL;
		goto cleanup;
	}

	read_trace_pipe();

cleanup:
	bpf_link__destroy(link);
	bpf_object__close(obj);
	return 0;
}

Makefile

编辑samples/bpf目录中的Makefile文件,在下方标红位置添加内容:


tprogs-y += xdp_redirect_map
tprogs-y += xdp_redirect
tprogs-y += xdp_monitor
tprogs-y += hello

xdp_redirect_map-objs := xdp_redirect_map_user.o $(XDP_SAMPLE)
xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
hello-objs := hello_user.o $(TRACE_HELPERS)

always-y += hbm_out_kern.o
always-y += hbm_edt_kern.o
always-y += xdpsock_kern.o
always-y += hello_kern.o

编译

回到内核源码根目录,再次运行以下命令:

make M=samples/bpf

如果出现如下图红框中的内容,不影响编译:
在这里插入图片描述
查看是否编译成功:

ls | grep "hello*"

在这里插入图片描述

运行

1)运行前线新起一个终端,用于测试:
在这里插入图片描述
2)在终端1中运行编译好的程序:
在这里插入图片描述
3)在终端2中执行任意命令,在终端1查看程序是否能够监测到,如果成功监测到新进程运行便会输出一条"Hello BPF World"
在这里插入图片描述

参考资料

  • eBPF文档 - What is eBPF?
  • bilibili - 高效入门eBPF
  • bilibili - BPF C编程入门
  • Ubuntu20安装最新版clang
  • 深入浅出 eBPF - 基于 Ubuntu 21.04 BPF 开发环境全攻略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/5036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 堆、大顶堆、小顶堆、堆排序

目录一、什么是堆&#xff1f;1.1 大顶堆1.2 小顶堆1.3 自定义greater1.4 堆索引的特点1.5 堆操作添加元素删除最大元素检查序列是否是堆检查部分序列为堆对堆进行排序升序降序问题&#xff1a;sort()和sort_heap()有什么区别二、排序算法&#xff1a;堆排序2.1 堆排序原理创建…

Bootstrap框架(全)

Bootstrap中文网&#xff1a;点我直达 Bootstrap菜鸟教程&#xff1a;点我直达 目录&#xff1a;&#xff08;一&#xff09;Bootstrap标准模板&#xff1a;&#xff08;二&#xff09;CSS样式区&#xff1a;一、布局容器&#xff1a;二、栅格系统&#xff1a;1.列组合&#x…

<Linux> 软件包管理器yum及编辑器vim的使用

文章目录一、yum的背景知识1. 商业生态2. 开源生态3. 软件生态本土化二、yum 的基本使用1. 查看软件包2. 安装、卸载、搜索软件实现Windows下和Linux下文件互传三、vim的基本使用1. vim 的基本概念2. vim 的基本操作2.1.模式间切换2.2.插入模式2.3.命令模式2.4.底行模式四、vim…

nginx(四十二)access阶段的模块学习

一 ngx_http_access ① 基本描述 场景&#xff1a; 限制某些ip来源的访问;基于ip进行限制细节点&#xff1a; 基于$remote_addr进行判断的 ② allow deny ③ 官方案例 说明&#xff1a; 按照配置文件中allow|deny的先后顺序,只要匹配上则停止继续匹配 ④ 经典应用场…

Rust机器学习之ndarray

Rust机器学习之ndarray 众所周知&#xff0c;Python之所以能成为机器学习的首选语言&#xff0c;与其丰富易用的库有很大关系。某种程度上可以说是诸如numpy、pandas、scikit-learn、matplotlib、pytorch、networks…等一系列科学计算和机器学习库成就了Python今天编程语言霸主…

Vue项目实战——【基于 Vue3.x + NodeJS】实现的课程表排课系统二(week-title)

文章目录基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09;初始化样式封装axios处理数据表格头部&#xff08;周几&#xff09;子组件写入根组件App.vue浅拿一下数据基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09; 初始化样式 src/ass…

当 AI 遇上 web3,会碰撞出什么火花?

2020 年之前&#xff0c;Web3 的路是创造者们铺好的。但 Web3 遇上了金融&#xff0c;这出乎了每个创造者的意料之外&#xff0c;稳定币、AMM 和借贷突其来地点燃了2020年的那个夏天。之后 Web3 又遇到了 NFT、游戏和元宇宙。不过因为技术限制&#xff0c;除了金融之外&#xf…

AVL树详解(附带旋转步骤图,手把手带你上分)

文章目录&#x1f44d; AVL树的概念先了解一下&#x1f601;AVL树节点的定义&#x1f60a;AVL树插入节点&#x1f91e;AVL树为什么要旋转&#x1f60d;AVL树的四种旋转左单旋右单旋左右双旋右左双旋❤️结语关于AVL树的讲解 &#x1f44d; AVL树的概念先了解一下 它的左右子树…

Scala005--Scala中的数据结构【集合】之数组

Scala中的数据结构和Java中一样&#xff0c;都有数组&#xff0c;列表&#xff0c;集合&#xff0c;映射。在Scala中与Java不同的是数组可以有可变数组&#xff0c;而不是一旦定义就不可以进行更改。我们来认识数组&#xff0c;并使用相应的代码去查看具体的实现效果。 目录 …

Linux命令从入门到实战 ----进程管理

文章目录kill终止进程pstree查看进程树netstat网络状态和端口占用信息crontab定时任务进程(process): 是正在执行的一个程序或命令&#xff0c;每一个进程都是一个运行的实体&#xff0c;都有自己的地址空间&#xff0c;并占用一定的系统资源 服务(service)&#xff1a;启动之后…

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ 文章目录深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据四、数据预处理1、加载数据2、检查数据3、配置数据集并进行归一化处理4、可视化数据五、构建…

xv6源码解析(三)——内存管理

01 内存管理 内存管理&#xff1a;通过编写物理页分配器&#xff0c;以链表管理所有空闲页帧&#xff0c; 实现了对物理页帧的回收与分配&#xff1b;在xv6系统sbrk内存管理方式的基础上&#xff0c;添加了进程用户空间非连续分区的分配。 内存管理参考链接 mmap 02 sbrk机制…

猿创征文|【Linux】Linux中的gdb调试器的使用

目录 一、什么是gdb&#xff1f; 二、gdb的安装 三、gdb的使用 1、只有debug版可以被调试 2、gdb的常用指令 2.1显示代码&#xff1a;l指令&#xff08;list指令&#xff09; 2.2设置断点&#xff1a;b指令&#xff08;break指令&#xff09; 2.3显示断点/禁用断点/开启…

Linux操作系统~进程fork到wait到底怎么用?

目录 1.fork() &#xff08;1&#xff09;.概念 &#xff08;2&#xff09;.fork的写时拷贝 &#xff08;3&#xff09;.fork常规用法 2.进程终止 &#xff08;1&#xff09;.进程退出场景/退出码 &#xff08;2&#xff09;.进程常见退出方法 1&#xff09;.exit函数 …

类与对象(中级)

目录 1. 包 1.1 包的概念 1.2 导入包中的类 1.3 自定义包 1.4 常见的包 2. Java三大特性 -- 封装 3. Java三大特性 -- 继承 3.1 继承的概念&#xff1a; 3.2 继承的语法 3.3 父类成员访问 3.3.1 子类中访问父类的成员变量 3.3.2 子类中访问父类的成员方法 4. supe…

数据挖掘(六) 层次聚类

数据挖掘&#xff08;六&#xff09; 层次聚类 1.层次聚类简介 层次聚类算法(Hierarchical Clustering)将数据集划分为一层一层的clusters&#xff0c;后面一层生成的clusters基于前面一层的结果。层次聚类算法一般分为两类&#xff1a; Divisive 层次聚类&#xff1a;又称自…

【nacos】5.1 spring cloud + Nacos 实现统一配置管理

1. 解决的问题&#xff1a; 配置动态更新配置集中式管理配置内容的安全性和权限不同部署环境下的配置 2. 环境&#xff1a; ideaspring cloudspring-cloud-alibaba nacosmavenmqtt &#xff08;客户端&#xff0c;服务器采用的是EMQ X 5.0 &#xff09; 3. pom依赖 3.1 父级…

皮卡丘python turtle海龟绘图(电力球版)附源代码

皮卡丘python turtle海龟绘图&#xff08;电力球版&#xff09;附源代码 本文目录&#xff1a; 一、皮卡丘python turtle海龟成品效果图 二、皮卡丘背景介绍 三、皮卡丘卡角色形象 四、愿你拥有一只皮卡丘 五、Python 海龟画图主要方法 &#xff08;1&#xff09;海龟画图…

Allegro在PCB上制作二维码和条形码操作指导

Allegro在PCB上制作二维码和条形码操作指导 当我们需要在PCB放置一个二维码或者条形码的时候,可以不需要额外去贴标签,可以直接在PCB上制作,如下图 制作出来的二维码和条形码是可以直接用扫码程序扫描的 具体操作步骤如下 首先要用was performance allegro productivity…

python Clickhouse 分布式表介绍和创建、插入和查询数据,以及解决遇到的问题

目录 一、分布式表和本地表 原理解析&#xff1a; 二、Clickhouse创建分布式表结构 三、python代码实现&#xff08;亲测有效&#xff09; 四、解决遇到的问题 解决 DB::Exception: Missing columns: wefgrgrfew while processing query: wefgrgrfew, required columns: …