mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-08 04:44:44 +08:00
Deploying to gh-pages from @ eunomia-bpf/bpf-developer-tutorial@633aac43f1 🚀
This commit is contained in:
49
print.html
49
print.html
@@ -1486,7 +1486,7 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
</code></pre>
|
||||
<p>这是一个 BPF(Berkeley Packet Filter)程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些地图(可以在 BPF 程序和内核的其他部分之间共享的数据结构)和两个函数:handle_entry 和 handle_exit。当内核进入和退出中断处理程序时,分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
|
||||
<p>这是一个 BPF(Berkeley Packet Filter)程序。BPF 程序是小型程序,可以直接在 Linux 内核中运行,用于过滤和操纵网络流量。这个特定的程序似乎旨在收集内核中中断处理程序的统计信息。它定义了一些 maps (可以在 BPF 程序和内核的其他部分之间共享的数据结构)和两个函数:handle_entry 和 handle_exit。当内核进入和退出中断处理程序时,分别执行这些函数。handle_entry 函数用于跟踪中断处理程序被执行的次数,而 handle_exit 则用于测量中断处理程序中花费的时间。</p>
|
||||
<h2 id="运行代码"><a class="header" href="#运行代码">运行代码</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>要编译这个程序,请使用 ecc 工具:</p>
|
||||
@@ -1632,6 +1632,15 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
<p>程序定义了一个名为handle_exec的SEC(static evaluator of code)函数,它被附加到跟踪进程执行的BPF程序上。该函数记录为该PID执行exec()的时间,并在指定了最小持续时间时不发出exec事件。如果未指定最小持续时间,则会从BPF ringbuf保留样本并使用数据填充样本,然后将其提交给用户空间进行后处理。</p>
|
||||
<p>程序还定义了一个名为handle_exit的SEC函数,它被附加到跟踪进程退出的BPF程序上。该函数会在确定PID和TID后计算进程的生命周期,然后根据min_duration_ns的值决定是否发出退出事件。如果进程的生命周期足够长,则会从BPF ringbuf保留样本并使用数据填充样本,然后将其提交给用户空间进行后处理。</p>
|
||||
<p>最后,主函数调用bpf_ringbuf_poll来轮询BPF ringbuf,并在接收到新的事件时处理该事件。这个函数会持续运行,直到全局标志exiting被设置为true,此时它会清理资源并退出。</p>
|
||||
<h2 id="install-dependencies"><a class="header" href="#install-dependencies">Install Dependencies</a></h2>
|
||||
<p>You will need <code>clang</code>, <code>libelf</code> and <code>zlib</code> to build the examples, package names may vary across distros.</p>
|
||||
<p>On Ubuntu/Debian, you need:</p>
|
||||
<pre><code class="language-shell">$ apt install clang libelf1 libelf-dev zlib1g-dev
|
||||
</code></pre>
|
||||
<p>On CentOS/Fedora, you need:</p>
|
||||
<pre><code class="language-shell">$ dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
|
||||
</code></pre>
|
||||
<h2 id="编译运行-1"><a class="header" href="#编译运行-1">编译运行</a></h2>
|
||||
<p>编译运行上述代码:</p>
|
||||
<pre><code class="language-console">$ ecc bootstrap.bpf.c bootstrap.h
|
||||
Compiling bpf object...
|
||||
@@ -1640,7 +1649,7 @@ $ sudo ecli run package.json
|
||||
Runing eBPF program...
|
||||
</code></pre>
|
||||
<h2 id="总结-10"><a class="header" href="#总结-10">总结</a></h2>
|
||||
<p>这是一个使用BPF的C程序,用于跟踪进程的启动和退出事件,并显示有关这些事件的信息。它通过使用argp API来解析命令行参数,并使用BPF地图存储进程的信息,包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序,以监视进程的执行和退出事件。最后,程序在终端中打印出启动和退出的进程信息。</p>
|
||||
<p>这是一个使用BPF的C程序,用于跟踪进程的启动和退出事件,并显示有关这些事件的信息。它通过使用argp API来解析命令行参数,并使用BPF maps 存储进程的信息,包括进程的PID和执行文件的文件名。程序还使用了SEC函数来附加BPF程序,以监视进程的执行和退出事件。最后,程序在终端中打印出启动和退出的进程信息。</p>
|
||||
<p>编译这个程序可以使用 ecc 工具,运行时可以使用 ecli 命令。更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:https://github.com/eunomia-bpf/eunomia-bpf</p>
|
||||
<div style="break-before: page; page-break-before: always;"></div><h1 id="ebpf入门实践教程使用-libbpf-bootstrap-开发程序统计-tcp-连接延时"><a class="header" href="#ebpf入门实践教程使用-libbpf-bootstrap-开发程序统计-tcp-连接延时">eBPF入门实践教程:使用 libbpf-bootstrap 开发程序统计 TCP 连接延时</a></h1>
|
||||
<h2 id="背景"><a class="header" href="#背景">背景</a></h2>
|
||||
@@ -1761,7 +1770,7 @@ cleanup:
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
<h2 id="编译运行-1"><a class="header" href="#编译运行-1">编译运行</a></h2>
|
||||
<h2 id="编译运行-2"><a class="header" href="#编译运行-2">编译运行</a></h2>
|
||||
<ul>
|
||||
<li><code>git clone https://github.com/libbpf/libbpf-bootstrap libbpf-bootstrap-cloned</code></li>
|
||||
<li>将 <a href="13-tcpconnlat/libbpf-bootstrap">libbpf-bootstrap</a>目录下的文件复制到 <code>libbpf-bootstrap-cloned/examples/c</code>下</li>
|
||||
@@ -1900,28 +1909,8 @@ cleanup:
|
||||
return 0;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="eunomia-测试-demo"><a class="header" href="#eunomia-测试-demo">Eunomia 测试 demo</a></h3>
|
||||
<p>使用命令行进行追踪:</p>
|
||||
<pre><code class="language-bash">$ sudo build/bin/Release/eunomia run tcpconnlat
|
||||
[sudo] password for yunwei:
|
||||
[2022-08-07 02:13:39.601] [info] eunomia run in cmd...
|
||||
[2022-08-07 02:13:40.534] [info] press 'Ctrl C' key to exit...
|
||||
PID COMM IP SRC DEST PORT LAT(ms) CONATINER/OS
|
||||
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
|
||||
3483 openresty 4 172.19.0.7 172.19.0.5 2379 0.08 docker-apisix_apisix_1
|
||||
3477 openresty 4 172.19.0.7 172.19.0.5 2379 0.04 docker-apisix_apisix_1
|
||||
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.05 docker-apisix_apisix_1
|
||||
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
|
||||
3478 openresty 4 172.19.0.7 172.19.0.5 2379 0.03 docker-apisix_apisix_1
|
||||
</code></pre>
|
||||
<p>还可以使用 eunomia 作为 prometheus exporter,在运行上述命令之后,打开 prometheus 自带的可视化面板:</p>
|
||||
<p>使用下述查询命令即可看到延时的统计图表:</p>
|
||||
<pre><code class="language-plain"> rate(eunomia_observed_tcpconnlat_v4_histogram_sum[5m])
|
||||
/
|
||||
rate(eunomia_observed_tcpconnlat_v4_histogram_count[5m])
|
||||
</code></pre>
|
||||
<p>结果:</p>
|
||||
<p><img src="13-tcpconnlat/tcpconnlat_p.png" alt="result" /></p>
|
||||
<h3 id="编译运行-3"><a class="header" href="#编译运行-3">编译运行</a></h3>
|
||||
<p>TODO</p>
|
||||
<h3 id="总结-12"><a class="header" href="#总结-12">总结</a></h3>
|
||||
<p>通过上面的实验,我们可以看到,tcpconnlat 工具的实现原理是基于内核的TCP连接的跟踪,并且可以跟踪到 tcp 连接的延迟时间;除了命令行使用方式之外,还可以将其和容器、k8s 等元信息综合起来,通过 <code>prometheus</code> 和 <code>grafana</code> 等工具进行网络性能分析。</p>
|
||||
<blockquote>
|
||||
@@ -2041,7 +2030,7 @@ static void handle_lost_events(void* ctx, int cpu, __u64 lost_cnt) {
|
||||
}
|
||||
</code></pre>
|
||||
<p>收到事件后所调用对应的处理函数并进行输出打印。</p>
|
||||
<h2 id="编译运行-2"><a class="header" href="#编译运行-2">编译运行</a></h2>
|
||||
<h2 id="编译运行-4"><a class="header" href="#编译运行-4">编译运行</a></h2>
|
||||
<ul>
|
||||
<li><code>git clone https://github.com/libbpf/libbpf-bootstrap libbpf-bootstrap-cloned</code></li>
|
||||
<li>将 <a href="14-tcpstates/libbpf-bootstrap">libbpf-bootstrap</a>目录下的文件复制到 <code>libbpf-bootstrap-cloned/examples/c</code>下</li>
|
||||
@@ -2175,7 +2164,7 @@ int BPF_PROG(tcp_rcv, struct sock *sk)
|
||||
<p>如果用户设置了"-show-ext"选项,则还会累加直方图的总延迟(latency)和计数(cnt)。</p>
|
||||
</li>
|
||||
</ol>
|
||||
<h2 id="编译运行-3"><a class="header" href="#编译运行-3">编译运行</a></h2>
|
||||
<h2 id="编译运行-5"><a class="header" href="#编译运行-5">编译运行</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>
|
||||
<pre><code class="language-shell">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
@@ -2459,7 +2448,7 @@ int BPF_KPROBE(free_enter, void *address)
|
||||
<p>gen_free_enter函数接收一个地址参数,该函数首先使用allocs map查找该地址对应的内存分配信息。如果未找到,则表示该地址没有被分配,该函数返回0。如果找到了对应的内存分配信息,则使用bpf_map_delete_elem从allocs map中删除该信息。</p>
|
||||
<p>接下来,调用update_statistics_del函数用于更新内存分配的统计信息,它接收堆栈ID和内存块大小作为参数。首先在combined_allocs map中查找堆栈ID对应的内存分配统计信息。如果没有找到,则输出一条日志,表示查找失败,并且函数直接返回。如果找到了对应的内存分配统计信息,则使用原子操作从内存分配统计信息中减去该内存块大小和1(表示减少了1个内存块)。这是因为堆栈ID对应的内存块数量减少了1,而堆栈ID对应的内存块总大小也减少了该内存块的大小。</p>
|
||||
<p>最后定义了一个bpf程序BPF_KPROBE(free_enter, void *address)会在进程调用free函数时执行。它会接收参数address,表示正在释放的内存块的地址,并调用gen_free_enter函数来处理该内存块的释放。</p>
|
||||
<h2 id="编译运行-4"><a class="header" href="#编译运行-4">编译运行</a></h2>
|
||||
<h2 id="编译运行-6"><a class="header" href="#编译运行-6">编译运行</a></h2>
|
||||
<pre><code class="language-console">$ git clone https://github.com/iovisor/bcc.git --recurse-submodules
|
||||
$ cd libbpf-tools/
|
||||
$ make memleak
|
||||
@@ -2583,7 +2572,7 @@ int BPF_PROG(restrict_connect, struct socket *sock, struct sockaddr *address, in
|
||||
<li>若请求地址为 1.1.1.1 则拒绝连接,否则允许连接;</li>
|
||||
</ul>
|
||||
<p>在程序运行期间,所有通过 socket 的连接操作都会被输出到 <code>/sys/kernel/debug/tracing/trace_pipe</code>。</p>
|
||||
<h2 id="编译运行-5"><a class="header" href="#编译运行-5">编译运行</a></h2>
|
||||
<h2 id="编译运行-7"><a class="header" href="#编译运行-7">编译运行</a></h2>
|
||||
<p>通过容器编译:</p>
|
||||
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
</code></pre>
|
||||
@@ -2665,7 +2654,7 @@ char __license[] SEC("license") = "GPL";
|
||||
</code></pre>
|
||||
<p>这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。</p>
|
||||
<p>总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。</p>
|
||||
<h2 id="编译运行-6"><a class="header" href="#编译运行-6">编译运行</a></h2>
|
||||
<h2 id="编译运行-8"><a class="header" href="#编译运行-8">编译运行</a></h2>
|
||||
<pre><code class="language-console">docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
</code></pre>
|
||||
<p>or compile with <code>ecc</code>:</p>
|
||||
|
||||
Reference in New Issue
Block a user