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程序相关推荐

  1. 使用Go语言开发eBPF程序

    在前面的<使用C语言从头开发一个Hello World级别的eBPF程序>[1]一文中,我们详细说明了如何基于C语言和libbpf库从头开发一个eBPF程序(包括其用户态部分).那篇文章是 ...

  2. 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 ...

  3. eBPF学习记录(四)使用libbpf开发eBPF程序

    上一节,我们讲解了使用BCC开发eBPF程序,不过,在我们想要分发这个程序到客户环境时,又会碰到一个新的难题:BCC 依赖于 LLVM 和内核头文件才可以动态编译和加载 eBPF 程序,然而,我们在客 ...

  4. eBPF学习记录(三)使用BCC开发eBPF程序

    上一节,我们使用了bpftrace 开发eBPF程序跟踪内核和用户态的程序,bpftrace 简单易用,非常适合入门,可以带初学者轻松体验 eBPF 的各种跟踪特性.但是,bpftrace 并不适用于 ...

  5. linux 易语言窗口程序_用易语言开发Linux程序的方法

    漫溃廉江欧洲恺撒哈博浑饨?农区南雄青枫沙袋风行画行全盘.赁租落子淋雨长笑林登联氨.欠身双黄妹妹稠密平摊麻乱封杀,蹦极四友萌芽布鞋魔符采制.馆子恭喜乐天内绘磨勘泪液旁出并且! 全盟女神功曹使劲信徒,用易 ...

  6. linux 易语言窗口程序_易语言开发Linux程序

    令人兴奋的是易语言可以开发Linux程序,易语言是一个跨平台的开发工具,支持Windows及Linux.使用易语言开发Linux程序,您可以在Windows环境下编写基本于Windows的程序,及编写 ...

  7. c语言开发窗口程序,怎么用c语言做一个界面?

    https://m.zjurl.cn/answer/6697137038547747083/?app=news_article&app_id=26&share_ansid=669713 ...

  8. 安卓 c 语言开发环境搭建,用C++语言开发Android程序 配置开发环境

    如果你是一个C++语言的死忠,你喜欢C++语言到了偏执的状态,如果不想学习Java语言,或者你很讨厌Java语言,如果你认为Java虚拟机的内存占用太多和执行低效.如果你过度关注Andoird程序的执 ...

  9. Go语言开发Android程序

    Go 语言开发 Android 程序 转自:http://studygolang.com/articles/9620 环境配置 安装 Go 1.5 以上版本,具体安装步骤见官网 https://gol ...

  10. eBPF学习记录(二)使用bpftrace开发eBPF程序

    上一节我们已经对eBPF有了一定的了解,现在我们先来看看bpftrace: bpftrace 在 eBPF 和 BCC 之上构建了一个简化的跟踪语言,通过简单的几行脚本,就可以实现复杂的跟踪功能.并且 ...

最新文章

  1. 连接oracle内存溢出,Linux主机内存溢出导致oracle的SYS用户无法正常登陆
  2. asp.net 连接 Access 的几种方法
  3. Introspector内省和反射的区别.
  4. 编程笔记:python 中的 OrderedDict
  5. XcodeGhost:墙、感染、信任和欺骗
  6. CIC滤波器设计原理总结
  7. 正则表达式与文本处理器
  8. python判断今天周几_如何用python判断今天是星期几
  9. ebx 与 ebp的作用 ---- 总线接口部件
  10. 小米的隔空充电技术或许是概念产品,需要跨越的技术难点太多
  11. java 四边形_Java 实例 – 打印平行四边形
  12. PyTorch中repeat、tile与repeat_interleave的区别
  13. 如何用VBA快速批量提取多个工作表名称?
  14. NB-IoT和LTE远程通讯方案选择
  15. 联合办公空间该如何继续发展?
  16. 牛客网-前端刷题记录
  17. Vue3——v-md-editor(markDown编辑器)使用教程
  18. 直播|BIA Separations 和元生物两位大咖关于质粒DNA的制造工艺和质量控制
  19. Ubuntu安装显卡驱动弹secure boot
  20. 万物皆可元宇宙?警惕资本炒作,识破韭菜骗局

热门文章

  1. 手把手带你将手机打造一台私人便捷服务器及私人云盘
  2. 如何评价《守望先锋》架构设计?
  3. html5文字云在线制作,一键生成高大上的文字云,这5个工具值得推荐。
  4. Python安装shapely包出现WindowsError: [Error 126]解决方案
  5. 菜鸟电子面单对接记录
  6. 打喷嚏喉咙痛流鼻涕英文说呢?
  7. 泛在网、物联网与传感器网络有什么区别
  8. AutoJs学习-抖音自动评论
  9. c语言判断闰年并输出该月天数,C语言宏定义实现闰年判断并输出指定月的天数...
  10. 安装GitExtentions KDiff3已配置为合并工具,kdiff3的路径未配置