Skip to content

Commit 4250a9a

Browse files
committed
docs: refine README files for clarity and conciseness across multiple tutorials
1 parent 6364a28 commit 4250a9a

File tree

8 files changed

+52
-131
lines changed

8 files changed

+52
-131
lines changed

src/10-hardirqs/README.zh.md

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,7 @@ softirqs 是软件中断处理程序。它们是内核中的一种底层异步
2020

2121
当内核处理 hardirqs 或 softirqs 时,这些 eBPF 程序会被执行,从而收集相关信息,如中断向量、中断处理程序的执行时间等。收集到的信息可以用于分析内核中的性能问题和其他与中断处理相关的问题。
2222

23-
为了捕获 hardirqs 和 softirqs,可以遵循以下步骤:
24-
25-
1. 在 eBPF 程序中定义用于存储中断信息的数据结构和映射。
26-
2. 编写 eBPF 程序,将其挂载到相应的内核函数上,以捕获 hardirqs 或 softirqs。
27-
3. 在 eBPF 程序中,收集中断处理程序的相关信息,并将这些信息存储在映射中。
28-
4. 在用户空间应用程序中,读取映射中的数据以分析和展示中断处理信息。
29-
30-
通过上述方法,我们可以在 eBPF 中使用 hardirqs 和 softirqs 捕获和分析内核中的中断事件,以识别潜在的性能问题和与中断处理相关的问题。
23+
为了捕获 hardirqs 和 softirqs,我们需要在 eBPF 程序中定义用于存储中断信息的数据结构和映射,编写 eBPF 程序并将其挂载到相应的内核函数上,收集中断处理程序的相关信息并存储在映射中,最后在用户空间应用程序中读取映射中的数据以分析和展示中断处理信息。通过这种方法,我们可以在 eBPF 中捕获和分析内核中的中断事件,以识别潜在的性能问题。
3124

3225
## hardirqs 代码实现
3326

@@ -166,78 +159,17 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
166159
char LICENSE[] SEC("license") = "GPL";
167160
```
168161
169-
这段代码是一个 eBPF 程序,用于捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。程序的主要目的是获取中断处理程序的名称、执行次数和执行时间,并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码。
170-
171-
1. 包含必要的头文件和定义数据结构:
172-
173-
```c
174-
#include <vmlinux.h>
175-
#include <bpf/bpf_core_read.h>
176-
#include <bpf/bpf_helpers.h>
177-
#include <bpf/bpf_tracing.h>
178-
#include "hardirqs.h"
179-
#include "bits.bpf.h"
180-
#include "maps.bpf.h"
181-
```
182-
183-
该程序包含了 eBPF 开发所需的标准头文件,以及用于定义数据结构和映射的自定义头文件。
184-
185-
2. 定义全局变量和映射:
186-
187-
```c
188-
189-
#define MAX_ENTRIES 256
190-
191-
const volatile bool filter_cg = false;
192-
const volatile bool targ_dist = false;
193-
const volatile bool targ_ns = false;
194-
const volatile bool do_count = false;
195-
196-
...
197-
```
162+
这段代码展示了如何使用 eBPF 捕获和分析硬件中断的执行信息。
198163
199-
该程序定义了一些全局变量,用于配置程序的行为。例如,`filter_cg` 控制是否过滤 cgroup,`targ_dist` 控制是否显示执行时间的分布等。此外,程序还定义了三个映射,分别用于存储 cgroup 信息、开始时间戳和中断处理程序的信息
164+
让我们看看代码的工作原理。程序定义了一些 `const volatile` 全局变量用于配置行为:`filter_cg` 控制是否过滤 cgroup,`targ_dist` 控制是否显示执行时间分布,`do_count` 控制是否只统计计数。程序使用三个映射:cgroup 过滤映射、per-CPU 的开始时间戳映射,以及存储中断处理信息的 hash 映射
200165
201-
3. 定义两个辅助函数 `handle_entry` 和 `handle_exit`
166+
核心逻辑在 `handle_entry` 和 `handle_exit` 两个函数中。在中断入口处,如果启用了计数模式,程序会直接增加中断计数;否则记录当前时间戳。在中断出口处,程序计算执行时间(当前时间减去开始时间),然后根据配置决定是累加总时间还是更新直方图槽位。
202167
203-
这两个函数分别在中断处理程序的入口和出口处被调用。`handle_entry` 记录开始时间戳或更新中断计数,`handle_exit` 计算中断处理程序的执行时间,并将结果存储到相应的信息映射中。
204-
205-
4. 定义 eBPF 程序的入口点:
206-
207-
```c
208-
209-
SEC("tp_btf/irq_handler_entry")
210-
int BPF_PROG(irq_handler_entry_btf, int irq, struct irqaction *action)
211-
{
212-
return handle_entry(irq, action);
213-
}
214-
215-
SEC("tp_btf/irq_handler_exit")
216-
int BPF_PROG(irq_handler_exit_btf, int irq, struct irqaction *action)
217-
{
218-
return handle_exit(irq, action);
219-
}
220-
221-
SEC("raw_tp/irq_handler_entry")
222-
int BPF_PROG(irq_handler_entry, int irq, struct irqaction *action)
223-
{
224-
return handle_entry(irq, action);
225-
}
226-
227-
SEC("raw_tp/irq_handler_exit")
228-
int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
229-
{
230-
return handle_exit(irq, action);
231-
}
232-
```
233-
234-
这里定义了四个 eBPF 程序入口点,分别用于捕获中断处理程序的入口和出口事件。`tp_btf` 和 `raw_tp` 分别代表使用 BPF Type Format(BTF)和原始 tracepoints 捕获事件。这样可以确保程序在不同内核版本上可以移植和运行。
235-
236-
Softirq 代码也类似,这里就不再赘述了。
168+
程序定义了四个 eBPF 入口点,使用 `tp_btf` 和 `raw_tp` 两种 tracepoint 类型。这种双重实现确保了程序在不同内核版本上的兼容性——较新的内核支持 BTF,较老的内核则使用原始 tracepoint。Softirq 代码也采用类似的模式。
237169
238170
## 运行代码
239171
240-
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
172+
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
241173
242174
要编译这个程序,请使用 ecc 工具:
243175
@@ -255,8 +187,6 @@ sudo ecli run ./package.json
255187

256188
## 总结
257189

258-
在本章节(eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件)中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。我们详细讲解了示例代码,包括如何定义数据结构、映射以及 eBPF 程序入口点,以及如何在中断处理程序的入口和出口处调用辅助函数来记录执行信息。
259-
260-
通过学习本章节内容,您应该已经掌握了如何在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件的方法,以及如何分析这些事件以识别内核中的性能问题和其他与中断处理相关的问题。这些技能对于分析和优化 Linux 内核的性能至关重要。
190+
在本章节中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序的执行信息。通过在中断处理程序的入口和出口处记录时间戳,我们可以测量中断处理时间,识别内核中的性能问题。
261191

262-
为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf> 。此外,我们还为您提供了完整的教程和源代码,您可以在 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考
192+
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或网站 <https://eunomia.dev/zh/tutorials/> 以获取更多示例和完整的教程

src/3-fentry-unlink/README.zh.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网
88

99
fentry(function entry)和 fexit(function exit)是 eBPF(扩展的伯克利包过滤器)中的两种探针类型,用于在 Linux 内核函数的入口和退出处进行跟踪。它们允许开发者在内核函数执行的特定阶段收集信息、修改参数或观察返回值。这种跟踪和监控功能在性能分析、故障排查和安全分析等场景中非常有用。
1010

11-
与 kprobes 相比,fentry 和 fexit 程序有更高的性能和可用性。在这个例子中,我们可以直接访问函数的指针参数,就像在普通的 C 代码中一样,而不需要使用各种读取帮助程序。fexit 和 kretprobe 程序最大的区别在于,fexit 程序可以访问函数的输入参数和返回值,而 kretprobe 只能访问返回值。从 5.5 内核开始,fentry 和 fexit 对 eBPF 程序可用。
11+
与 kprobes 相比,fentry 和 fexit 程序有更高的性能和可用性。它们的运行速度大约是 kprobes 的 10 倍,因为使用了 BPF trampoline 机制而不是基于断点的旧方法。在这个例子中,我们可以直接访问函数的指针参数,就像在普通的 C 代码中一样,而不需要使用 `BPF_CORE_READ` 这样的读取帮助程序。
12+
13+
fexit 和 kretprobe 程序最大的区别在于,fexit 程序可以同时访问函数的输入参数和返回值,而 kretprobe 只能访问返回值。这让你可以在一个地方看到完整的函数执行情况。从 5.5 内核开始(x86),fentry 和 fexit 对 eBPF 程序可用。
1214

1315
> arm64 内核版本需要 6.0
1416
>
@@ -50,18 +52,15 @@ int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret)
5052
}
5153
```
5254
53-
这段程序是用 C 语言编写的 eBPF(扩展的伯克利包过滤器)程序,它使用 BPF 的 fentry 和 fexit 探针来跟踪 Linux 内核函数 `do_unlinkat`。在这个教程中,我们将以这段程序作为示例,让您学会如何在 eBPF 中使用 fentry 监测捕获 unlink 系统调用
55+
这段程序是用 C 语言编写的 eBPF 程序,它使用 BPF 的 fentry 和 fexit 探针来跟踪 Linux 内核函数 `do_unlinkat`。
5456
55-
程序包含以下部分:
57+
让我们看看代码的工作原理。首先我们包含了必要的头文件:vmlinux.h 用于访问内核数据结构,bpf_helpers.h 包含 eBPF 帮助函数,bpf_tracing.h 用于跟踪相关功能。然后定义了许可证信息 "Dual BSD/GPL",这是内核加载 eBPF 程序所必需的。
5658
57-
1. 包含头文件:包括 vmlinux.h(用于访问内核数据结构)、bpf/bpf_helpers.h(包含eBPF帮助函数)、bpf/bpf_tracing.h(用于eBPF跟踪相关功能)。
58-
2. 定义许可证:这里定义了一个名为 `LICENSE` 的字符数组,包含许可证信息“Dual BSD/GPL”。
59-
3. 定义 fentry 探针:我们定义了一个名为 `BPF_PROG(do_unlinkat)` 的 fentry 探针,该探针在 `do_unlinkat` 函数的入口处被触发。这个探针获取当前进程的 PID(进程ID)并将其与文件名一起打印到内核日志。
60-
4. 定义 fexit 探针:我们还定义了一个名为 `BPF_PROG(do_unlinkat_exit)` 的 fexit 探针,该探针在 `do_unlinkat` 函数的退出处被触发。与 fentry 探针类似,这个探针也会获取当前进程的 PID 并将其与文件名和返回值一起打印到内核日志。
59+
fentry 探针附加到 `do_unlinkat` 函数的入口点。注意我们可以直接访问 `name->name` 而不需要任何特殊的帮助函数——这是使用 fentry 而不是 kprobes 的好处之一。我们获取当前进程 PID,然后将其与文件名一起打印到内核日志。
6160
62-
通过这个示例,您可以学习如何在 eBPF 中使用 fentry 和 fexit 探针来监控和捕获内核函数调用,例如在本教程中的 unlink 系统调用
61+
fexit 探针在函数返回时触发,可以同时访问原始参数(dfd 和 name)和返回值(ret)。这让你可以完整地看到函数做了什么。如果 ret 是 0,文件删除成功;如果是负数,说明出错了,你可以看到错误代码
6362
64-
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
63+
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
6564
6665
编译运行上述代码:
6766

src/4-opensnoop/README.zh.md

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,17 @@ int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter* ctx
3636
char LICENSE[] SEC("license") = "GPL";
3737
```
3838
39-
这段 eBPF 程序实现了:
39+
让我们看看代码的工作原理。首先我们引入了必要的头文件:vmlinux.h 包含内核数据结构的定义,bpf_helpers.h 包含 eBPF 所需的辅助函数。
4040
41-
1. 引入头文件:<vmlinux.h> 包含了内核数据结构的定义,<bpf/bpf_helpers.h> 包含了 eBPF 程序所需的辅助函数。
42-
2. 定义全局变量 `pid_target`,用于过滤指定进程 ID。这里设为 0 表示捕获所有进程的 sys_openat 调用。
43-
3. 使用 `SEC` 宏定义一个 eBPF 程序,关联到 tracepoint "tracepoint/syscalls/sys_enter_openat"。这个 tracepoint 会在进程发起 `sys_openat` 系统调用时触发。
44-
4. 实现 eBPF 程序 `tracepoint__syscalls__sys_enter_openat`,它接收一个类型为 `struct trace_event_raw_sys_enter` 的参数 `ctx`。这个结构体包含了关于系统调用的信息。
45-
5. 使用 `bpf_get_current_pid_tgid()` 函数获取当前进程的 PID 和 TID(线程 ID)。由于我们只关心 PID,所以将其值右移 32 位赋值给 `u32` 类型的变量 `pid`。
46-
6. 检查 `pid_target` 变量是否与当前进程的 pid 相等。如果 `pid_target` 不为 0 且与当前进程的 pid 不相等,则返回 `false`,不对该进程的 `sys_openat` 调用进行捕获。
47-
7. 使用 `bpf_printk()` 函数打印捕获到的进程 ID 和 `sys_openat` 调用的相关信息。这些信息可以在用户空间通过 BPF 工具查看。
48-
8. 将程序许可证设置为 "GPL",这是运行 eBPF 程序的必要条件。
41+
全局变量 `pid_target` 用于过滤指定进程 ID。注意这里使用了 `const volatile` 关键字 `const` 防止 eBPF 程序修改它,而 `volatile` 告诉编译器这个值可能会被用户空间程序修改。设为 0 表示捕获所有进程的 sys_openat 调用。
42+
43+
我们使用 `SEC` 宏将程序关联到 tracepoint "tracepoint/syscalls/sys_enter_openat"。这个 tracepoint 会在进程发起 `sys_openat` 系统调用时触发,函数接收一个 `struct trace_event_raw_sys_enter` 类型的参数,包含了系统调用的信息。
44+
45+
在函数中,我们使用 `bpf_get_current_pid_tgid()` 获取当前进程的 PID 和 TID(线程 ID),由于我们只关心 PID,所以将返回值右移 32 位,然后检查 `pid_target`——如果设置了特定值且不匹配当前 pid,就直接返回,不进行捕获,最后使用 `bpf_printk()` 打印捕获到的进程 ID。
4946
5047
这个 eBPF 程序可以通过 libbpf 或 eunomia-bpf 等工具加载到内核并执行。它将捕获指定进程(或所有进程)的 sys_openat 系统调用,并在用户空间输出相关信息。
5148
52-
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。完整代码请查看 <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/4-opensnoop> 。
49+
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它。完整代码请查看 <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/4-opensnoop> 。
5350
5451
编译运行上述代码:
5552

src/5-uprobe-bashreadline/README.zh.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ Uprobe 在内核态 eBPF 运行时,也可能产生比较大的性能开销,
1616

1717
## 使用 uprobe 捕获 bash 的 readline 函数调用
1818

19-
uprobe 是一种用于捕获用户空间函数调用的 eBPF 的探针,我们可以通过它来捕获用户空间程序调用的系统函数。
19+
uprobe 特别适合在以下场景使用:你需要跟踪特定应用程序的行为,但无法修改源代码或重新编译程序;你想要调试复杂的用户空间问题,比如追踪库函数调用;或者你需要进行安全审计,监控敏感函数的调用。
20+
21+
为什么不能直接使用 kprobe?因为像 readline 这样的函数是在用户空间库中实现的,不是内核函数。uprobe 让我们能够在不修改程序的情况下,动态地插入探测点到任何用户空间二进制文件中。
2022

2123
例如,我们可以使用 uprobe 来捕获 bash 的 readline 函数调用,从而获取用户在 bash 中输入的命令行。示例代码如下:
2224

@@ -107,7 +109,10 @@ BPF_KRETPROBE(printret, const void *ret)
107109
bpf_printk("PID %d (%s) read: %s ", pid, comm, str);
108110
```
109111

110-
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。
112+
113+
关键的部分是 `bpf_probe_read_user_str` 我们需要使用这个特殊的辅助函数来安全地从用户空间内存读取字符串,不能直接解引用 `ret` 指针,因为 eBPF 程序运行在内核空间,直接访问用户空间内存会导致错误。这个函数确保了安全的跨空间内存访问。
114+
115+
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它。
111116

112117
编译运行上述代码:
113118

src/6-sigsnoop/README.zh.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,15 @@ int kill_exit(struct trace_event_raw_sys_exit *ctx)
8787
char LICENSE[] SEC("license") = "Dual BSD/GPL";
8888
```
8989
90-
上面的代码定义了一个 eBPF 程序,用于捕获进程发送信号的系统调用,包括 kill、tkill 和 tgkill。它通过使用 tracepoint 来捕获系统调用的进入和退出事件,并在这些事件发生时执行指定的探针函数,例如 probe_entry 和 probe_exit
90+
这个程序展示了一个重要的 eBPF 模式:如何在系统调用的入口和出口之间保存和关联信息
9191
92-
在探针函数中,我们使用 bpf_map 存储捕获的事件信息,包括发送信号的进程 ID、接收信号的进程 ID、信号值和进程的可执行文件名称。在系统调用退出时,我们将获取存储在 bpf_map 中的事件信息,并使用 bpf_printk 打印进程 ID、进程名称、发送的信号和系统调用的返回值
92+
为什么需要 hash map?因为我们需要在两个不同的探针函数之间共享数据,当系统调用进入时(`sys_enter_kill`),我们知道目标进程 ID 和信号值,但还不知道操作是否成功。当系统调用退出时(`sys_exit_kill`),我们得到了返回值,但已经失去了对参数的访问。hash map 让我们能够在入口处保存信息,然后在出口处检索它
9393
94-
最后,我们还需要使用 SEC 宏来定义探针,并指定要捕获的系统调用的名称,以及要执行的探针函数
94+
看看代码如何工作:`probe_entry` 在系统调用进入时触发,我们使用线程 ID 作为键,将事件信息存储到 hash map 中。`probe_exit` 在系统调用返回时触发,使用相同的线程 ID 查找之前保存的信息,添加返回值,然后打印完整的事件。最后删除 map 条目以避免内存泄漏
9595
96-
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。
96+
这种模式非常常见——任何时候你需要关联系统调用的参数和返回值,都可以使用这个技术。
97+
98+
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它。
9799
98100
编译运行上述代码:
99101

0 commit comments

Comments
 (0)