eBPF原理介绍与编程实践

注:本文包括了ebpf的原理介绍、流程分析、相关资料链接、工具编写实战等,可以选择感兴趣的部分直接阅读;鉴于作者语文水平有限,很多地方描述可能不清楚,有错误或疑问欢迎指出交流

1.    初步了解

1.1 eBPF

eBPF是一个用RISC指令集设计的VM,他可以通过运行BPF程序来跟踪内核函数、内存等。

用Linux社区大牛Gregg的话来讲,“eBPF does to Linux what JavaScript does to HTML”。

1.2 BCC

BCC,全名BPF Compiler Collection,在 github.com/iovisor/bcc,  这个工具集提供了很多样例的tracing工具可以直接使用,同时也提供了可用于开发这些工具的python、lua接口。

安装步骤: https://github.com/iovisor/bcc/blob/master/INSTALL.md

BCC工具教程: https://github.com/iovisor/bcc

BCC python教程: https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md

BCC reference: https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md

1.3 bpftrace

bpftrace 是一组bcc的封装工具,比起bcc是面向一些更复杂、庞大的问题而言,bpftrace进一步封装,调用了bcc接口来实现了通过一行脚本来定位一些特定场景下的问题。

地址github.com/iovisor/bpftrace

安装步骤: https://github.com/iovisor/bpftrace/blob/master/INSTALL.md

Bpftrace编程教程: https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md

Bpftrace reference: https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md

2.    eBPF 原理

注: 以下全部代码基于Linux 5.8

2.1 工作流程

编译器把C代码编译成eBPF字节码(这里有点像jvm机制,只不过这个ebpf的vm在内核)。

用户态程序把字节码、程序类型发给内核,以此来决定哪些内核区域(代码、内存)可以被这个程序访问(通过调用 bpf_prog_alloc 然后把字节码复制到 prog->insns, 见图2.1.1 bpf_prog struct)。

内核对字节码做一个检查,保证这个程序是安全的(没有越界、死循环等等),代码在kernel/bpf/verifier.c。

内核通过JIT编译字节码到机器码,然后通过kprobe、tracepoint来把这些代码插入到对应的位置。

这些被插入的bpf程序代码会把数据写到他们自己的ringbuffers或者key-value maps(后面会讲到,ebpf存储数据的自定义内核数据结构)。

用户态读取这些ringbuffers或者maps来获取想要的数据。

图 3.2.2 静态 tracepoint 定义

通过查看 /sys/kernel/debug/tracing/events/*moudle_name*/*tracepoint_name* 去看有哪些支持的tracepoint。

因此在本例中,需要查看block相关的tracepoint,可以找到tracepoint block_rq_issue,而而且我们可以通过/sys/kernel/debug/tracing/events/block/block_rq_issue/format 了解参数格式,见图3.2.3。

图 3.2.3 arguments format of block_rq_issue

在图3.2.3中可以看到一个bytes域,如果需要记录bytes,可以直接在我们的程序中去使用它。

在此之前,需要先想办法保存他,最简单直接的方法是自己定义一个map去存储,但是从前面ebpf原理的分析我们知道map是分离的,如果新搞一个会很影响性能,因此我们需要重写当前的map, 见图3.2.4.

图 3.2.4 修改data type同时存储ts和bytes

现在我们需要获取bytes(在 TRACEPOINT_PROBE 中是通过 args->*在format文件中的参数名* 获取) 来更新map,还有一点需要注意的是我们使用 dev(id) + sector(id) 来作为 key (这个是来自Gregg’s 对写IO requests相关工具开发者的建议), 见图 3.2.5.

图 3.2.5 probe block_rq_issue body代码

对于 block_rq_complete 写法非常类似,见图 3.2.6.

图 3.2.6 probe block_rq_complete body代码

现在ebpf部分程序已经改完了,最后我们需要修改一下python部分来增加一下新增的输出,这里我们仅需要将输出的trace_point的最后一个域调用split,第一部分打印我们刚加的bytes,第二部分打印原来的耗时。(因为这里是顺序打印, 可以参考图3.2.6重的bpf_trace_printk ), python部分见图 3.2.7.

图 3.2.7 python 输出格式

这里对 disksnoop.py的改动已经完成了,在本例中其实改动非常简单,仅起到抛砖引玉的作用,对于更多的其他功能,需要研究有没有相关的tracepoint,如果没有就只能做动态trace了。

4 参考文献

http://www.brendangregg.com/blog/2019-01-01/learn-ebpf-tracing.html

http://www.brendangregg.com/ebpf.html

https://github.com/iovisor/bcc/blob/master/docs/tutorial.md

https://github.com/iovisor/bcc/blob/master/docs/reference_guide

https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md

https://www.collabora.com/news-and-blog/blog/2019/04/05/an-ebpf-overview-part-1-introduction/

https://www.collabora.com/news-and-blog/blog/2019/04/15/an-ebpf-overview-part-2-machine-and-bytecode/

https://blog.csdn.net/hjkfcz/article/details/104916719

https://blog.yadutaf.fr/2016/03/30/turn-any-syscall-into-event-introducing-ebpf-kernel-probes/

https://blog.aquasec.com/intro-ebpf-tracing-containers

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
eBPF原理介绍与编程实践
注:本文包括了ebpf的原理介绍、流程分析、相关资料链接、工具编写实战等,可以选择感兴趣的部分直接阅读;鉴于作者语文水平有限,很多地方描述可能不清楚,有错误或疑...
<<上一篇
下一篇>>