Linux C语言实践eBPF
手动编译了解过程
通过对关键步骤make M=samples/bpf的实践,我们已经可以编译出内核源码中提供的ebpf样例。但这还不够我们充分地理解这个编译过程,我们将这编译过程拆解一下,拆解成可以一步步执行的那种,首先是环境准备:
$ sudo apt-get install linux-source
$ cd /usr/src/
$ ls
linux-source-5.15.0.tar.bz2 linux-headers-5.15.0-43-generic linux-source-5.15.0
$ sudo tar -xvf linux-source-5.15.0.tar.bz2 -C /tmp/
$ cd /tmp/linux-source-5.15.0/
内核源码树环境准备
$ sudo make oldconfig
$ sudo make init
$ sudo make headers_install
进入tools目录编译依赖库
$ sudo cd tools/lib/bpf/
编译依赖库为object中间件
$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o libbpf.o libbpf.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o bpf.o bpf.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o btf.o btf.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o nlattr.o nlattr.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o strset.o strset.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o str_error.o str_error.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o hashmap.o hashmap.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o libbpf_probes.o libbpf_probes.c$ sudo gcc -I. -I/tmp/linux-source-5.15.0/samples/bpf/../..//tools/include -I/tmp/linux-source-5.15.0/samples/bpf/../..//tools/include/uapi -fvisibility=hidden -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D"BUILD_STR(s)=#s" -c -o /tmp/linux-source-5.15.0/samples/bpf/libbpf/staticobjs/gen_loader.o gen_loader.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o relo_core.o relo_core.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o netlink.o netlink.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o linker.o linker.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o xsk.o xsk.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o ringbuf.o ringbuf.c$ sudo gcc -g -DHAVE_LIBELF_MMAP_SUPPORT -DCOMPAT_NEED_REALLOCARRAY -fPIC -I. -I/tmp/linux-source-5.15.0/tools/bpf/ -I/tmp/linux-source-5.15.0/tools/arch/x86/include/uapi -I/tmp/linux-source-5.15.0/tools/include/ -I/tmp/linux-source-5.15.0/tools/perf -D"BUILD_STR(s)=#s" -c -o btf_dump.o btf_dump.c
将编译好的object文件链接为libbpf.a静态库
$ cd /tmp/linux-source-5.15.0/tools/lib/bpf
$ sudo ar rcs libbpf.a bpf.o btf.o libbpf.o nlattr.o str_error.o strset.o gen_loader.o libbpf_probes.o relo_core.o ringbuf.o linker.o netlink.o xsk.o btf_dump.o
编译用户态测试程序
$ cd /tmp/linux-source-5.15.0
$ sudo gcc -I./usr/include -I./tools/testing/selftests/bpf/ -I/tmp/linux-source-5.15.0/samples/bpf/libbpf/include -I./tools/include -I./tools/perf -DHAVE_ATTR_TEST=0 -c -o samples/bpf/trace_output_user.o samples/bpf/trace_output_user.c
$ sudo gcc -I./usr/include -I./tools/testing/selftests/bpf/ -I/tmp/linux-source-5.15.0/samples/bpf/libbpf/include -I./tools/include -I./tools/perf -DHAVE_ATTR_TEST=0 -o samples/bpf/trace_output samples/bpf/trace_output_user.o /tmp/linux-source-5.15.0/samples/bpf/libbpf/libbpf.a -lelf -lz -lrt
编译示例程序的第二部分,即需要给内核虚拟机运行的,由bpf_load()加载的字节码
$ cd /tmp/linux-source-5.15.0
$ sudo clang -g -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/8/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -Isamples/bpf -Isamples/bpf/libbpf/include/ -Isamples/bpf/libbpf/ -I./tools/testing/selftests/bpf/ -D__KERNEL__ -D__BPF_TRACING__ -D__TARGET_ARCH_x86 -O2 -emit-llvm -c samples/bpf/trace_output_kern.c -o - | sudo llc -march=bpf -filetype=obj -o samples/bpf/trace_output_kern.o
$ sudo llvm-strip -g /tmp/linux-source-5.15.0/samples/bpf/trace_output_kern.o
测试
$ cd /tmp/linux-source-5.15.0/samples/bpf
$ sudo ./trace_output
recv 236472 events per sec
100013+0 records in
100013+0 records out
51206656 bytes (51 MB, 49 MiB) copied, 0.420891 s, 122 MB/s
解析源代码
BPF程序结构图:
由上图可知BPF程序要由两部分组成用户态执行程序和BPF bytecode。
trace_output示例的量部分如下,其中trace_output_user.c为用户态执行程序,trace_output_kern.c为BPF bytecode,所以trace_output_user.c使用gcc编译,trace_output_kern.c使用clang编译。
$ ls samples/bpf/ | grep trace_output
trace_output_kern.c
trace_output_user.c
trace_output_user.c里的主要内容
int main(int argc, char **argv)
{struct perf_buffer_opts pb_opts = {};struct bpf_link *link = NULL;struct bpf_program *prog;struct perf_buffer *pb;struct bpf_object *obj;int map_fd, ret = 0;char filename[256];FILE *f;snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);obj = bpf_object__open_file(filename, NULL);if (libbpf_get_error(obj)) {fprintf(stderr, "ERROR: opening BPF object file failed\n");return 0;}/* load BPF program */if (bpf_object__load(obj)) {fprintf(stderr, "ERROR: loading BPF object file failed\n");goto cleanup;}map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");if (map_fd < 0) {fprintf(stderr, "ERROR: finding a map in obj file failed\n");goto cleanup;}prog = bpf_object__find_program_by_name(obj, "bpf_prog1");if (libbpf_get_error(prog)) {fprintf(stderr, "ERROR: finding a prog in obj 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;}pb_opts.sample_cb = print_bpf_output;pb = perf_buffer__new(map_fd, 8, &pb_opts);ret = libbpf_get_error(pb);if (ret) {printf("failed to setup perf_buffer: %d\n", ret);return 1;}f = popen("taskset 1 dd if=/dev/zero of=/dev/null", "r");(void) f;start_time = time_get_ns();while ((ret = perf_buffer__poll(pb, 1000)) >= 0 && cnt < MAX_CNT) {}kill(0, SIGINT);cleanup:bpf_link__destroy(link);bpf_object__close(obj);return ret;
}
其中bpf_object__open_file打开的字节码文件trace_output_kern.c里的内容如下:
struct {__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);__uint(key_size, sizeof(int));__uint(value_size, sizeof(u32));__uint(max_entries, 2);
} my_map SEC(".maps");SEC("kprobe/" SYSCALL(sys_write))
int bpf_prog1(struct pt_regs *ctx)
{struct S {u64 pid;u64 cookie;} data;data.pid = bpf_get_current_pid_tgid();data.cookie = 0x12345678;bpf_perf_event_output(ctx, &my_map, 0, &data, sizeof(data));return 0;
}char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;
参考链接
相关文章:

Linux C语言实践eBPF
手动编译了解过程 通过对关键步骤make Msamples/bpf的实践,我们已经可以编译出内核源码中提供的ebpf样例。但这还不够我们充分地理解这个编译过程,我们将这编译过程拆解一下,拆解成可以一步步执行的那种,首先是环境准备ÿ…...

垃圾回收标记阶段算法
1.标记阶段的目的 主要是在GC在前,判断出哪些是有用的对象,哪些是需要回收的对象,只有被标记为垃圾对象,GC才会对其进行垃圾回收。判断对象是否为垃圾对象的两种方式:引用计数算法和可达性分析算法。 2.引用计数算法…...

泰晓科技发布 Linux Lab v1.2 正式版
导读近日消息,Linux Lab 是一套用于 Linux 内核学习、开发和测试的即时实验室,官方称其“可以极速搭建和使用,功能强大,用法简单”。 自去年 12 月份发布 Linux Lab v1.1 后,v1.2 正式版目前已经发布于 GitHub 及 Gite…...

王道数据结构-代码实操1(全注解版)
#include<stdio.h>void loveyou(int n){ // 传入参数类型为int型,在此函数中表示为n;返回值类型为void,即没有返回值; int i1; //定义了一个整数型变量i,且只在loveyou函数中有用;while(i…...

flink写入到kafka 大坑解析。
1.kafka能不能发送null消息? 能! 2 flink能不能发送null消息到kafka? 不能! public static void main(String[] args) throws Exception {StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(…...
MATLAB算法实战应用案例精讲-【深度学习】预训练模型-Subword
目录 前言 Subword 1. Subword介绍 分词器是做什么的? 为什么需要分词? 分词方法...
【HarmonyOS】实现从视频提取音频并保存到pcm文件功能(API6 Java)
【关键字】 视频提取类Extractor、视频编解码、保存pcm文件 【写在前面】 在使用API6开发HarmonyOS应用时,通常会开发一些音视频媒体功能,这里介绍如何从视频中提取音频保存到pcm文件功能,生成pcm音频文件后,就可使用音频播放类…...

Linux:shell命令运行原理和权限的概念
文章目录 shell和kernelshell的概念和原理Linux的权限文件的权限文件的类型文件的权限管理权限的实战应用 shell和kernel 从狭义上来讲,Linux是一个操作系统,我们叫它叫kernel,意思是核心,核心的意思顾名思义,就是最关…...
Javascript -- 数组prototype方法探究
一、数组prototype方法探究 1、不改变原数组 1. concat() 这个是数组拼接方法,可以将两个数组或多个数组拼接并返回一个包含两个数组或多个数组内容的新数组,不会改变原数组 方法里面理论上可以写入n个参数, const arr [1,2]; var str …...

android stduio 打开工程后直接报Connection refused解决
报错如下:Connection refused 解决方案: 打开gradle-wrapper.properties修改distributionUrl 将: distributionUrlhttp\://localhost/gradle/distributions/gradle-6.5-bin.zip 替换为: distributionUrlhttps\://services.gradle.org/distributions/gradle-6.5-bin.zip 错…...

搜索与图论(一)
一、DFS与BFS 1.1深度优先搜索(DFS) DFS不具有最短性 //排列数字问题 #include<iostream> using namespace std;const int N 10; int n; int path[N]; bool st[N];void dfs(int u) {if(u n){for(int i 0;i < n;i) printf("%d",path[i]);puts("&qu…...

百题千解计划【CSDN每日一练】“小明投篮,罚球线投球可得一分”(附解析+多种实现方法:Python、Java、C、C++、C#、Go、JavaScript)
这个心上人,还不知道在哪里,感觉明天就会出现。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌟[2] 2022年度博客之星人工智能领域TOP4🌟 🏅[3] 阿里云社区特邀专家博主🏅 🏆[4] CSDN-人工智能领域优质创作者�…...
lemon框架开发笔记
lemon框架开发笔记 JudgeUtils.isBlank() 字符串为 null 或者 "" ----返回true JudgeUtils.isNotBlankAll() 字符串全部不为 null 或者 "" ----返回true JudgeUtils.isBlankAll() 字符串全部为 null 或者 "" ----返回true// isBlank 是在isEmpt…...

Spark SQL快速入门
1. 了解Spark SQL 1.1 什么是Spark SQL Spark SQL是spark的一个模块,用于处理海量的结构化数据。 1.2 Spark SQL有什么特点?优点是什么? 特点: Spark SQL支持读取和写入多种格式的数据源,包括Parquet、JSON、CSV、…...

linux+Jenkins+飞书机器人发送通知(带签名)
文章目录 如何使用在linux 上安装python 环境发送消息python脚本把脚本上传倒linux上 jenkins 上执行脚本 如何使用 自定义机器人使用指南飞书官网https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot 在linux 上安装python 环境 yum install python3 python…...
react hooks
1 useEffect(setup,dependencies) 使用object.is来比较每个依赖项和它先前的值 依赖项为空数组的effect不会在组件任何props和state发生改变时重新运行 当useEffect依赖于外部传入props对象时,容易造成死循环 需要对依赖对象进行深比较 import { isEqual } from…...
一起学数据结构(1)——复杂度
目录 1. 时间复杂度: 1.1 时间复杂度的概念: 1.2 时间复杂度的表示及计算: 1.3 较为复杂的时间复杂度的计算: 2. 空间复杂度: 2.1 空间复杂度的概念: 2.2 空间复杂度的计算: 1. 时间复杂度…...

<el-date-picker>组件选择开始时间,结束时间自动延长30min
背景:选择开始时间,结束时间自动增加30分钟,结束时间也可重新选择,如图: <el-form-item label"预约开始时间" prop"value1"><el-date-pickersize"large"v-model"ruleForm…...
eslint-webpack-plugin
说明:现在eslint已经弃用了eslint-loader,如果要安装来使用的话,会报错,烦死人 大概的报错信息如下: ERROR in ./src/index.js Module build failed (from ./node_modules/eslint-loader/dist/cjs.js): TypeError: Cannot read …...

logback中文一直是乱码,logback中文问号
logback一直是乱码 方案一加上UTF-8 方案二我这边方案一不行 在启动参数加上 -Dfile.encodingutf-8 这个竟然就可以了...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...