如何使用c语言开发ebpf程序
1、前言:
最近开始陆续负责ebpf相关的项目,于是对ebpf相关知识内容进行复习。
ebpf的优势是避免了痛苦的kernel开发工作,如果没有ebpf之前,我们如果想做内核可视化,必须要开发kernel的驱动,但是如果我们有了ebpf,开发的程序就可以很好的和kernel隔离开,避免了kernel的崩坏。
学习回顾之前的博客:
ebpf c 学习_序冢--磊的博客-CSDN博客
相关查阅资料:
https://ebpf.io/summit-2020-slides/eBPF_Summit_2020-Lightning-Lorenzo_Fontana-Debugging_the_BPF_Virtual_Machine.pdf
https://qmo.fr/docs/talk_20200202_debugging_ebpf.pdf
开发ebpf程序你需要掌握哪些知识
1、linux 内核相关的知识
2、掌握常用的ebpf 工具
3、熟悉libbpf
4、有一定c语言开发功底
ebpf 主要有两部分程序组成,一部分是kernel程序,需要使用clang 编译成字节码,另一部分是用户态程序,负责加载字节码
ebpf 编译采用clang + llvm虚拟机嵌入到内核中
本篇笔记探究的点为:
1、我们使用ebpf 可以在哪里插桩
2、用户态程序和bpf程序是如何通信的
后续会继续对内容进行逐步探索,探索ebpf在云原生等各个领域的应用
2、我们使用ebpf 可以在哪里插桩
这里单用kprobe 为例子:
bpftrace -l "kprobe:*"
这样就可以列出所有插桩点,比如我们要对sys_write进行插装就可以在后面的c程序中
SEC("kprobe/__x64_sys_write")
3、用户态和内核态程序如何通信
1、文件通信
2、使用ringBuffer
我看到libbpf里可以使用ringBuffer 通信,甚至可能还支持epoll多路复用,日后有需要还会研究,因为工作中主要使用golang 和 ebpfc 通信,所以暂时只看个大概
LIBBPF_API struct ring_buffer *
ring_buffer__new(int map_fd, ring_buffer_sample_fn sample_cb, void *ctx,const struct ring_buffer_opts *opts);
LIBBPF_API void ring_buffer__free(struct ring_buffer *rb);
LIBBPF_API int ring_buffer__add(struct ring_buffer *rb, int map_fd,ring_buffer_sample_fn sample_cb, void *ctx);
LIBBPF_API int ring_buffer__poll(struct ring_buffer *rb, int timeout_ms);
LIBBPF_API int ring_buffer__consume(struct ring_buffer *rb);
LIBBPF_API int ring_buffer__epoll_fd(const struct ring_buffer *rb);
代码里还有ring_buffer__epoll_fd,后续研究下如何使用这些api实现用户和内核态通信的
3、使用map
map类似与共享内存
案例代码:
kernel ebpf 程序部分:
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include <unistd.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>char LICENSE[] SEC("license") = "Dual BSD/GPL";int my_pid = 0;struct{__uint(type, BPF_MAP_TYPE_HASH);__uint(max_entries, 8192);__type(key, pid_t);__type(value, __u64);
}mapTest SEC(".maps");SEC("kprobe/__x64_sys_write")
int handle_tp(void *ctx)
{pid_t pid = bpf_get_current_pid_tgid() >> 32;__u64 ts = bpf_ktime_get_ns();int ret = bpf_map_update_elem(&mapTest, &pid, &ts, BPF_ANY);char msg[] = "hello:%d;ret:%d;pidpr:%p\n";bpf_trace_printk(msg, sizeof(msg), pid, ret, &pid
);return 0;
}
这里我们程序是当有程序使用write一类的函数就会触发本函数,然后我们拿到进程id和系统时间,放到map里,map定义:
struct{__uint(type, BPF_MAP_TYPE_HASH);__uint(max_entries, 8192);__type(key, pid_t);__type(value, __u64);
}mapTest SEC(".maps");
放到map 以后会写到文件里
#define DEBUGFS "/sys/kernel/debug/tracing/trace"
用户态程序会去一直读这个文件,读到内容后去遍历map
将c程序编译为字节码:
/usr/bin/clang-14 -g -O2 -target bpf -D__TARGET_ARCH_x86_64 -c data.c -o kernel_write.o
用户部分程序:
主要是加载字节码,以及读ebpf kernel部分程序写入文件和map里的内容
#include <bpf/bpf.h>
#include "common.h"
/* SPDX-License-Identifier: GPL-2.0 */
static const char *__doc__ = "Simple XDP prog doing XDP_PASS\n";#define DEBUGFS "/sys/kernel/debug/tracing/"static void bump_memlock_rlimit(void)
{struct rlimit rlim_new = {.rlim_cur = RLIM_INFINITY,.rlim_max = RLIM_INFINITY,};if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) {fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!%s\n", strerror(errno));exit(1);}
}void read_trace_pipe(struct bpf_object *obj)
{int trace_fd;trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);printf("%s\n", DEBUGFS "trace_pipe");if (trace_fd < 0) {printf("%s\n", strerror(errno));return;}struct bpf_map * map = bpf_object__find_map_by_name(obj, "mapTest");if (!map) {return;}int map_fd = bpf_map__fd(map);pid_t* look_key = NULL;pid_t next_key = 0;pid_t value = 0;while (1) {static char buf[4096];ssize_t sz;sz = read(trace_fd, buf, sizeof(buf) - 1);if (sz > 0) {buf[sz] = 0;puts(buf);while (bpf_map_get_next_key(map_fd, look_key, &next_key) != -1) {printf("%d\n", next_key);if (look_key != NULL) {printf("look:%d\n", *look_key);}look_key = &next_key;}sleep(4);}}if (map_fd < 0) {return;}
// pid_t pid = getpid();
// printf("true pid:%d\n", pid);
// unsigned long long value1 = 10;
// int nn = bpf_map_update_elem(map_fd, &pid, (const void *) (&value1), BPF_ANY);
// printf("nn:%d\n", nn);// int result = bpf_map_lookup_elem(map_fd, (const void *) &pid, (void *) &value1);// printf("pid:%d\n", pid);}int main(int argc, char **argv)
{char msg[255];bump_memlock_rlimit();struct bpf_object * obj = bpf_object__open_file("./kernel_write.o", NULL);if (libbpf_get_error(obj)) {fprintf(stderr, "open object file error!\n");return -1;}struct bpf_map * prog = bpf_object__find_program_by_name(obj, "handle_tp");if (!prog) {fprintf(stderr, "ERROR: finding a prog in obj file failed\n");return -1;}int ret = bpf_object__load(obj);if (ret != 0) {fprintf(stderr, "load object file error!%s\n", libbpf_strerror(errno, msg, sizeof(msg)));return -1;}struct bpf_link* link = bpf_program__attach(prog);if (libbpf_get_error(link)) {fprintf(stderr, "ERROR: bpf_program__attach failed\n");return -1;}read_trace_pipe(obj);return 0;
}
如何使用c语言开发ebpf程序相关推荐
- 使用Go语言开发eBPF程序
在前面的<使用C语言从头开发一个Hello World级别的eBPF程序>[1]一文中,我们详细说明了如何基于C语言和libbpf库从头开发一个eBPF程序(包括其用户态部分).那篇文章是 ...
- eBPF系列学习(4)了解libbpf、CO-RE (Compile Once – Run Everywhe) | 使用go开发ebpf程序(云原生利器cilium ebpf )
文章目录 一.了解libbpf 1. BPF的可移植性CO-RE (Compile Once – Run Everywhere) BPF 可移植性面临的问题 BPF的可移植性CO-RE (Compil ...
- eBPF学习记录(四)使用libbpf开发eBPF程序
上一节,我们讲解了使用BCC开发eBPF程序,不过,在我们想要分发这个程序到客户环境时,又会碰到一个新的难题:BCC 依赖于 LLVM 和内核头文件才可以动态编译和加载 eBPF 程序,然而,我们在客 ...
- eBPF学习记录(三)使用BCC开发eBPF程序
上一节,我们使用了bpftrace 开发eBPF程序跟踪内核和用户态的程序,bpftrace 简单易用,非常适合入门,可以带初学者轻松体验 eBPF 的各种跟踪特性.但是,bpftrace 并不适用于 ...
- linux 易语言窗口程序_用易语言开发Linux程序的方法
漫溃廉江欧洲恺撒哈博浑饨?农区南雄青枫沙袋风行画行全盘.赁租落子淋雨长笑林登联氨.欠身双黄妹妹稠密平摊麻乱封杀,蹦极四友萌芽布鞋魔符采制.馆子恭喜乐天内绘磨勘泪液旁出并且! 全盟女神功曹使劲信徒,用易 ...
- linux 易语言窗口程序_易语言开发Linux程序
令人兴奋的是易语言可以开发Linux程序,易语言是一个跨平台的开发工具,支持Windows及Linux.使用易语言开发Linux程序,您可以在Windows环境下编写基本于Windows的程序,及编写 ...
- c语言开发窗口程序,怎么用c语言做一个界面?
https://m.zjurl.cn/answer/6697137038547747083/?app=news_article&app_id=26&share_ansid=669713 ...
- 安卓 c 语言开发环境搭建,用C++语言开发Android程序 配置开发环境
如果你是一个C++语言的死忠,你喜欢C++语言到了偏执的状态,如果不想学习Java语言,或者你很讨厌Java语言,如果你认为Java虚拟机的内存占用太多和执行低效.如果你过度关注Andoird程序的执 ...
- Go语言开发Android程序
Go 语言开发 Android 程序 转自:http://studygolang.com/articles/9620 环境配置 安装 Go 1.5 以上版本,具体安装步骤见官网 https://gol ...
- eBPF学习记录(二)使用bpftrace开发eBPF程序
上一节我们已经对eBPF有了一定的了解,现在我们先来看看bpftrace: bpftrace 在 eBPF 和 BCC 之上构建了一个简化的跟踪语言,通过简单的几行脚本,就可以实现复杂的跟踪功能.并且 ...
最新文章
- 连接oracle内存溢出,Linux主机内存溢出导致oracle的SYS用户无法正常登陆
- asp.net 连接 Access 的几种方法
- Introspector内省和反射的区别.
- 编程笔记:python 中的 OrderedDict
- XcodeGhost:墙、感染、信任和欺骗
- CIC滤波器设计原理总结
- 正则表达式与文本处理器
- python判断今天周几_如何用python判断今天是星期几
- ebx 与 ebp的作用 ---- 总线接口部件
- 小米的隔空充电技术或许是概念产品,需要跨越的技术难点太多
- java 四边形_Java 实例 – 打印平行四边形
- PyTorch中repeat、tile与repeat_interleave的区别
- 如何用VBA快速批量提取多个工作表名称?
- NB-IoT和LTE远程通讯方案选择
- 联合办公空间该如何继续发展?
- 牛客网-前端刷题记录
- Vue3——v-md-editor(markDown编辑器)使用教程
- 直播|BIA Separations 和元生物两位大咖关于质粒DNA的制造工艺和质量控制
- Ubuntu安装显卡驱动弹secure boot
- 万物皆可元宇宙?警惕资本炒作,识破韭菜骗局
热门文章
- 手把手带你将手机打造一台私人便捷服务器及私人云盘
- 如何评价《守望先锋》架构设计?
- html5文字云在线制作,一键生成高大上的文字云,这5个工具值得推荐。
- Python安装shapely包出现WindowsError: [Error 126]解决方案
- 菜鸟电子面单对接记录
- 打喷嚏喉咙痛流鼻涕英文说呢?
- 泛在网、物联网与传感器网络有什么区别
- AutoJs学习-抖音自动评论
- c语言判断闰年并输出该月天数,C语言宏定义实现闰年判断并输出指定月的天数...
- 安装GitExtentions KDiff3已配置为合并工具,kdiff3的路径未配置