This commit is contained in:
yunwei37
2023-05-06 19:44:00 +00:00
parent 943e990f5f
commit e10d1a77f1
8 changed files with 1091 additions and 270 deletions

View File

@@ -148,9 +148,9 @@
<p>eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。</p>
<p>本文是 eBPF 入门开发实践教程的第八篇,在 eBPF 中使用 exitsnoop 监控进程退出事件。</p>
<h2 id="ring-buffer"><a class="header" href="#ring-buffer">ring buffer</a></h2>
<p>现在有一个新的 BPF 数据结构可用BPF 环形缓冲区ring buffer。它解决了 BPF perf buffer当今从内核向用户空间发送数据的事实上的标准的内存效率和事件重排问题同时达到或超过了它的性能。它既提供了与 perf buffer 兼容以方便迁移,又有新的保留/提交API具有更好的可用性。另外合成和真实世界的基准测试表明在几乎所有的情况下所以考虑将其作为从BPF程序向用户空间发送数据的默认选择。</p>
<h3 id="bpf-ringbuf-vs-bpf-perfbuf"><a class="header" href="#bpf-ringbuf-vs-bpf-perfbuf">BPF ringbuf vs BPF perfbuf</a></h3>
<p>今天,只要BPF程序需要将收集到的数据发送到用户空间进行后处理和记录它通常会使用BPF perf bufferperfbuf来实现。Perfbuf 是每个CPU循环缓冲区的集合它允许在内核和用户空间之间有效地交换数据。它在实践中效果很好但由于其按CPU设计它有两个主要的缺点在实践中被证明是不方便的内存的低效使用和事件的重新排序。</p>
<p>现在有一个新的 BPF 数据结构可用eBPF 环形缓冲区ring buffer。它解决了 BPF perf buffer当今从内核向用户空间发送数据的事实上的标准的内存效率和事件重排问题同时达到或超过了它的性能。它既提供了与 perf buffer 兼容以方便迁移,又有新的保留/提交API具有更好的可用性。另外合成和真实世界的基准测试表明在几乎所有的情况下所以考虑将其作为从BPF程序向用户空间发送数据的默认选择。</p>
<h3 id="ebpf-ringbuf-vs-ebpf-perfbuf"><a class="header" href="#ebpf-ringbuf-vs-ebpf-perfbuf">eBPF ringbuf vs eBPF perfbuf</a></h3>
<p>只要 BPF 程序需要将收集到的数据发送到用户空间进行后处理和记录,它通常会使用 BPF perf bufferperfbuf来实现。Perfbuf 是每个CPU循环缓冲区的集合它允许在内核和用户空间之间有效地交换数据。它在实践中效果很好但由于其按CPU设计它有两个主要的缺点在实践中被证明是不方便的内存的低效使用和事件的重新排序。</p>
<p>为了解决这些问题从Linux 5.8开始BPF提供了一个新的BPF数据结构BPF map。BPF环形缓冲区ringbuf。它是一个多生产者、单消费者MPSC队列可以同时在多个CPU上安全共享。</p>
<p>BPF ringbuf 支持来自 BPF perfbuf 的熟悉的功能:</p>
<ul>
@@ -175,11 +175,11 @@
#define MAX_FILENAME_LEN 127
struct event {
int pid;
int ppid;
unsigned exit_code;
unsigned long long duration_ns;
char comm[TASK_COMM_LEN];
int pid;
int ppid;
unsigned exit_code;
unsigned long long duration_ns;
char comm[TASK_COMM_LEN];
};
#endif /* __BOOTSTRAP_H */
@@ -233,10 +233,19 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
return 0;
}
</code></pre>
<p>这段代码是一个 BPF 程序,用于监控 Linux 系统中的进程退出事件。</p>
<p>该程序通过注册一个 tracepoint来监控进程退出事件。Tracepoint 是一种内核特性,允许内核模块获取特定事件的通知。在本程序中,注册的 tracepoint 是“tp/sched/sched_process_exit”表示该程序监控的是进程退出事件。</p>
<p>当系统中发生进程退出事件时BPF 程序会捕获该事件并调用“handle_exit”函数来处理它。该函数首先检查当前退出事件是否是进程退出事件而不是线程退出事件然后在 BPF 环形缓冲区“rb”中保留一个事件结构体并填充该结构体中的其他信息例如进程 ID、进程名称、退出代码和退出信号等信息。最后该函数还会调用 BPF 的“perf_event_output”函数将捕获的事件发送给用户空间程序</p>
<p>总而言之,这段代码是一个 BPF 程序,用于监控 Linux 系统中的进程退出事件.</p>
<p>这段代码展示了如何使用 exitsnoop 监控进程退出事件并使用 ring buffer 向用户态打印输出:</p>
<ol>
<li>首先,我们引入所需的头文件和 exitsnoop.h</li>
<li>定义一个名为 &quot;LICENSE&quot; 的全局变量,内容为 &quot;Dual BSD/GPL&quot;,这是 eBPF 程序的许可证要求。</li>
<li>定义一个名为 rb 的 BPF_MAP_TYPE_RINGBUF 类型的映射,它将用于将内核空间的数据传输到用户空间。指定 max_entries 为 256 * 1024代表 ring buffer 的最大容量。</li>
<li>定义一个名为 handle_exit 的 eBPF 程序,它将在进程退出事件触发时执行。传入一个名为 ctx 的 trace_event_raw_sched_process_template 结构体指针作为参数。</li>
<li>使用 bpf_get_current_pid_tgid() 函数获取当前任务的 PID 和 TID。对于主线程PID 和 TID 相同;对于子线程,它们是不同的。我们只关心进程(主线程)的退出,因此在 PID 和 TID 不同时返回 0忽略子线程退出事件。</li>
<li>使用 bpf_ringbuf_reserve 函数为事件结构体 e 在 ring buffer 中预留空间。如果预留失败,返回 0。</li>
<li>使用 bpf_get_current_task() 函数获取当前任务的 task_struct 结构指针。</li>
<li>将进程相关信息填充到预留的事件结构体 e 中包括进程持续时间、PID、PPID、退出代码以及进程名称。</li>
<li>最后,使用 bpf_ringbuf_submit 函数将填充好的事件结构体 e 提交到 ring buffer之后在用户空间进行处理和输出。</li>
</ol>
<p>这个示例展示了如何使用 exitsnoop 和 ring buffer 在 eBPF 程序中捕获进程退出事件并将相关信息传输到用户空间。这对于分析进程退出原因和监控系统行为非常有用。</p>
<h2 id="compile-and-run"><a class="header" href="#compile-and-run">Compile and Run</a></h2>
<p>eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。</p>
<p>Compile:</p>
@@ -262,8 +271,8 @@ TIME PID PPID EXIT_CODE DURATION_NS COMM
21:40:09 42059 42054 0 0 cat
</code></pre>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>本文介绍了如何使用 eunomia-bpf 开发一个简单的 BPF 程序,该程序可以监控 Linux 系统中的进程退出事件, 并将捕获的事件通过 ring buffer 发送给用户空间程序。在本文中,我们使用 eunomia-bpf 编译运行了这个例子。如果你想了解更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:<a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a></p>
<p>完整的教程和源代码已经全部开源,可以在 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 中查看。</p>
<p>本文介绍了如何使用 eunomia-bpf 开发一个简单的 BPF 程序,该程序可以监控 Linux 系统中的进程退出事件, 并将捕获的事件通过 ring buffer 发送给用户空间程序。在本文中,我们使用 eunomia-bpf 编译运行了这个例子。</p>
<p>为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<a href="https://github.com/eunomia-bpf/eunomia-bpf">https://github.com/eunomia-bpf/eunomia-bpf</a> 。此外,我们还为您提供了完整的教程和源代码,可以在 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考</p>
</main>