docs: refine README files for clarity and conciseness across multiple tutorials

This commit is contained in:
yunwei37
2025-10-03 17:05:21 -07:00
parent 6364a288f2
commit 4250a9a441
8 changed files with 52 additions and 131 deletions

View File

@@ -20,14 +20,7 @@ softirqs 是软件中断处理程序。它们是内核中的一种底层异步
当内核处理 hardirqs 或 softirqs 时,这些 eBPF 程序会被执行,从而收集相关信息,如中断向量、中断处理程序的执行时间等。收集到的信息可以用于分析内核中的性能问题和其他与中断处理相关的问题。
为了捕获 hardirqs 和 softirqs可以遵循以下步骤:
1. 在 eBPF 程序中定义用于存储中断信息的数据结构和映射。
2. 编写 eBPF 程序,将其挂载到相应的内核函数上,以捕获 hardirqs 或 softirqs。
3. 在 eBPF 程序中,收集中断处理程序的相关信息,并将这些信息存储在映射中。
4. 在用户空间应用程序中,读取映射中的数据以分析和展示中断处理信息。
通过上述方法,我们可以在 eBPF 中使用 hardirqs 和 softirqs 捕获和分析内核中的中断事件,以识别潜在的性能问题和与中断处理相关的问题。
为了捕获 hardirqs 和 softirqs我们需要在 eBPF 程序中定义用于存储中断信息的数据结构和映射,编写 eBPF 程序并将其挂载到相应的内核函数上,收集中断处理程序的相关信息并存储在映射中,最后在用户空间应用程序中读取映射中的数据以分析和展示中断处理信息。通过这种方法,我们可以在 eBPF 中捕获和分析内核中的中断事件,以识别潜在的性能问题。
## hardirqs 代码实现
@@ -166,78 +159,17 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
char LICENSE[] SEC("license") = "GPL";
```
这段代码是一个 eBPF 程序,用于捕获和分析内核中硬件中断处理程序hardirqs的执行信息。程序的主要目的是获取中断处理程序的名称、执行次数和执行时间并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码
这段代码展示了如何使用 eBPF 捕获和分析硬件中断的执行信息
1. 包含必要的头文件和定义数据结构:
让我们看看代码的工作原理。程序定义了一些 `const volatile` 全局变量用于配置行为:`filter_cg` 控制是否过滤 cgroup`targ_dist` 控制是否显示执行时间分布,`do_count` 控制是否只统计计数。程序使用三个映射cgroup 过滤映射、per-CPU 的开始时间戳映射,以及存储中断处理信息的 hash 映射。
```c
#include <vmlinux.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "hardirqs.h"
#include "bits.bpf.h"
#include "maps.bpf.h"
```
核心逻辑在 `handle_entry``handle_exit` 两个函数中。在中断入口处,如果启用了计数模式,程序会直接增加中断计数;否则记录当前时间戳。在中断出口处,程序计算执行时间(当前时间减去开始时间),然后根据配置决定是累加总时间还是更新直方图槽位。
该程序包含了 eBPF 开发所需的标准头文件,以及用于定义数据结构和映射的自定义头文件
2. 定义全局变量和映射:
```c
#define MAX_ENTRIES 256
const volatile bool filter_cg = false;
const volatile bool targ_dist = false;
const volatile bool targ_ns = false;
const volatile bool do_count = false;
...
```
该程序定义了一些全局变量,用于配置程序的行为。例如,`filter_cg` 控制是否过滤 cgroup`targ_dist` 控制是否显示执行时间的分布等。此外,程序还定义了三个映射,分别用于存储 cgroup 信息、开始时间戳和中断处理程序的信息。
3. 定义两个辅助函数 `handle_entry` 和 `handle_exit`
这两个函数分别在中断处理程序的入口和出口处被调用。`handle_entry` 记录开始时间戳或更新中断计数,`handle_exit` 计算中断处理程序的执行时间,并将结果存储到相应的信息映射中。
4. 定义 eBPF 程序的入口点:
```c
SEC("tp_btf/irq_handler_entry")
int BPF_PROG(irq_handler_entry_btf, int irq, struct irqaction *action)
{
return handle_entry(irq, action);
}
SEC("tp_btf/irq_handler_exit")
int BPF_PROG(irq_handler_exit_btf, int irq, struct irqaction *action)
{
return handle_exit(irq, action);
}
SEC("raw_tp/irq_handler_entry")
int BPF_PROG(irq_handler_entry, int irq, struct irqaction *action)
{
return handle_entry(irq, action);
}
SEC("raw_tp/irq_handler_exit")
int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
{
return handle_exit(irq, action);
}
```
这里定义了四个 eBPF 程序入口点,分别用于捕获中断处理程序的入口和出口事件。`tp_btf` 和 `raw_tp` 分别代表使用 BPF Type FormatBTF和原始 tracepoints 捕获事件。这样可以确保程序在不同内核版本上可以移植和运行。
Softirq 代码也类似,这里就不再赘述了。
程序定义了四个 eBPF 入口点,使用 `tp_btf``raw_tp` 两种 tracepoint 类型。这种双重实现确保了程序在不同内核版本上的兼容性——较新的内核支持 BTF较老的内核则使用原始 tracepoint。Softirq 代码也采用类似的模式
## 运行代码
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
要编译这个程序,请使用 ecc 工具:
@@ -255,8 +187,6 @@ sudo ecli run ./package.json
## 总结
在本章节eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件)中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序hardirqs的执行信息。我们详细讲解了示例代码包括如何定义数据结构、映射以及 eBPF 程序入口点,以及如何在中断处理程序的入口和出口处调用辅助函数来记录执行信息
在本章节中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序的执行信息。通过在中断处理程序的入口和出口处记录时间戳,我们可以测量中断处理时间,识别内核中的性能问题
通过学习本章节内容,您应该已经掌握了如何在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件的方法,以及如何分析这些事件以识别内核中的性能问题和其他与中断处理相关的问题。这些技能对于分析和优化 Linux 内核的性能至关重要。
为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf> 。此外,我们还为您提供了完整的教程和源代码,您可以在 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考。
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或网站 <https://eunomia.dev/zh/tutorials/> 以获取更多示例和完整的教程。

View File

@@ -8,7 +8,9 @@ eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网
fentryfunction entry和 fexitfunction exit是 eBPF扩展的伯克利包过滤器中的两种探针类型用于在 Linux 内核函数的入口和退出处进行跟踪。它们允许开发者在内核函数执行的特定阶段收集信息、修改参数或观察返回值。这种跟踪和监控功能在性能分析、故障排查和安全分析等场景中非常有用。
与 kprobes 相比fentry 和 fexit 程序有更高的性能和可用性。在这个例子中,我们可以直接访问函数的指针参数,就像在普通的 C 代码中一样而不需要使用各种读取帮助程序。fexit 和 kretprobe 程序最大的区别在于fexit 程序可以访问函数的输入参数和返回值,而 kretprobe 只能访问返回值。从 5.5 内核开始fentry 和 fexit 对 eBPF 程序可用
与 kprobes 相比fentry 和 fexit 程序有更高的性能和可用性。它们的运行速度大约是 kprobes 的 10 倍,因为使用了 BPF trampoline 机制而不是基于断点的旧方法。在这个例子中,我们可以直接访问函数的指针参数,就像在普通的 C 代码中一样,而不需要使用 `BPF_CORE_READ` 这样的读取帮助程序
fexit 和 kretprobe 程序最大的区别在于fexit 程序可以同时访问函数的输入参数和返回值,而 kretprobe 只能访问返回值。这让你可以在一个地方看到完整的函数执行情况。从 5.5 内核开始x86fentry 和 fexit 对 eBPF 程序可用。
> arm64 内核版本需要 6.0
>
@@ -50,18 +52,15 @@ int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret)
}
```
这段程序是用 C 语言编写的 eBPF(扩展的伯克利包过滤器)程序,它使用 BPF 的 fentry 和 fexit 探针来跟踪 Linux 内核函数 `do_unlinkat`在这个教程中,我们将以这段程序作为示例,让您学会如何在 eBPF 中使用 fentry 监测捕获 unlink 系统调用。
这段程序是用 C 语言编写的 eBPF 程序,它使用 BPF 的 fentry 和 fexit 探针来跟踪 Linux 内核函数 `do_unlinkat`
程序包含以下部分:
让我们看看代码的工作原理。首先我们包含了必要的头文件vmlinux.h 用于访问内核数据结构bpf_helpers.h 包含 eBPF 帮助函数bpf_tracing.h 用于跟踪相关功能。然后定义了许可证信息 "Dual BSD/GPL",这是内核加载 eBPF 程序所必需的。
1. 包含头文件:包括 vmlinux.h用于访问内核数据结构、bpf/bpf_helpers.h包含eBPF帮助函数、bpf/bpf_tracing.h用于eBPF跟踪相关功能
2. 定义许可证:这里定义了一个名为 `LICENSE` 的字符数组包含许可证信息“Dual BSD/GPL”。
3. 定义 fentry 探针:我们定义了一个名为 `BPF_PROG(do_unlinkat)` 的 fentry 探针,该探针在 `do_unlinkat` 函数的入口处被触发。这个探针获取当前进程的 PID进程ID并将其与文件名一起打印到内核日志。
4. 定义 fexit 探针:我们还定义了一个名为 `BPF_PROG(do_unlinkat_exit)` 的 fexit 探针,该探针在 `do_unlinkat` 函数的退出处被触发。与 fentry 探针类似,这个探针也会获取当前进程的 PID 并将其与文件名和返回值一起打印到内核日志。
fentry 探针附加到 `do_unlinkat` 函数的入口点。注意我们可以直接访问 `name->name` 而不需要任何特殊的帮助函数——这是使用 fentry 而不是 kprobes 的好处之一。我们获取当前进程 PID然后将其与文件名一起打印到内核日志
通过这个示例,您可以学习如何在 eBPF 中使用 fentry 和 fexit 探针来监控和捕获内核函数调用,例如在本教程中的 unlink 系统调用
fexit 探针在函数返回时触发可以同时访问原始参数dfd 和 name和返回值ret。这让你可以完整地看到函数做了什么。如果 ret 是 0文件删除成功如果是负数说明出错了你可以看到错误代码
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
编译运行上述代码:

View File

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

View File

@@ -16,7 +16,9 @@ Uprobe 在内核态 eBPF 运行时,也可能产生比较大的性能开销,
## 使用 uprobe 捕获 bash 的 readline 函数调用
uprobe 是一种用于捕获用户空间函数调用的 eBPF 的探针,我们可以通过它来捕获用户空间程序调用的系统函数
uprobe 特别适合在以下场景使用:你需要跟踪特定应用程序的行为,但无法修改源代码或重新编译程序;你想要调试复杂的用户空间问题,比如追踪库函数调用;或者你需要进行安全审计,监控敏感函数的调用
为什么不能直接使用 kprobe因为像 readline 这样的函数是在用户空间库中实现的不是内核函数。uprobe 让我们能够在不修改程序的情况下,动态地插入探测点到任何用户空间二进制文件中。
例如,我们可以使用 uprobe 来捕获 bash 的 readline 函数调用,从而获取用户在 bash 中输入的命令行。示例代码如下:
@@ -107,7 +109,10 @@ BPF_KRETPROBE(printret, const void *ret)
bpf_printk("PID %d (%s) read: %s ", pid, comm, str);
```
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。
关键的部分是 `bpf_probe_read_user_str` 我们需要使用这个特殊的辅助函数来安全地从用户空间内存读取字符串,不能直接解引用 `ret` 指针,因为 eBPF 程序运行在内核空间,直接访问用户空间内存会导致错误。这个函数确保了安全的跨空间内存访问。
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它。
编译运行上述代码:

View File

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

View File

@@ -72,13 +72,15 @@ int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx
char LICENSE[] SEC("license") = "GPL";
```
段代码定义了个 eBPF 程序,用于捕获进程执行 execve 系统调用的入口
个程序展示了如何使用 perf event array 将数据从内核空间传输到用户空间
在入口程序中,我们首先获取了当前进程的进程 ID 和用户 ID然后通过 bpf_get_current_task 函数获取了当前进程的 task_struct 结构体,并通过 bpf_probe_read_str 函数读取了进程名称。最后,我们通过 bpf_perf_event_output 函数将进程执行事件输出到 perf buffer
为什么需要 perf buffer之前的例子我们使用 `bpf_printk` 打印到 trace_pipe这种方式调试很方便但对生产环境不够灵活perf buffer 让我们能够高效地将结构化数据从内核传输到用户空间程序,在那里可以进行复杂的处理、过滤和格式化输出
使用这段代码,我们就可以捕获 Linux 内核中进程执行的事件, 并分析进程的执行情况
看看代码如何工作:我们首先定义了一个 `BPF_MAP_TYPE_PERF_EVENT_ARRAY` 类型的 map。在 tracepoint 中我们收集进程信息PID、PPID、UID 和命令名。使用 `BPF_CORE_READ` 从内核结构体中安全地读取父进程 ID使用 `bpf_probe_read_str` 读取命令行参数。最后,`bpf_perf_event_output` 将整个 event 结构体发送到用户空间
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
用户空间程序(这里是 ecli会接收这些事件并格式化输出。这种模式让我们可以在用户空间做更复杂的处理比如过滤、聚合或者写入数据库
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它。
使用容器编译:

View File

@@ -106,23 +106,17 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
}
```
这段代码展示了如何使用 exitsnoop 监控进程退出事件并使用 ring buffer 向用户态打印输出:
这段代码展示了 ring buffer 的 reserve/submit 模式,这是一种高效的数据传输方式。
1. 首先,我们引入所需的头文件和 exitsnoop.h
2. 定义一个名为 "LICENSE" 的全局变量,内容为 "Dual BSD/GPL",这是 eBPF 程序的许可证要求。
3. 定义一个名为 rb 的 BPF_MAP_TYPE_RINGBUF 类型的映射,它将用于将内核空间的数据传输到用户空间。指定 max_entries 为 256 * 1024代表 ring buffer 的最大容量。
4. 定义一个名为 handle_exit 的 eBPF 程序,它将在进程退出事件触发时执行。传入一个名为 ctx 的 trace_event_raw_sched_process_template 结构体指针作为参数。
5. 使用 bpf_get_current_pid_tgid() 函数获取当前任务的 PID 和 TID。对于主线程PID 和 TID 相同;对于子线程,它们是不同的。我们只关心进程(主线程)的退出,因此在 PID 和 TID 不同时返回 0忽略子线程退出事件。
6. 使用 bpf_ringbuf_reserve 函数为事件结构体 e 在 ring buffer 中预留空间。如果预留失败,返回 0。
7. 使用 bpf_get_current_task() 函数获取当前任务的 task_struct 结构指针。
8. 将进程相关信息填充到预留的事件结构体 e 中包括进程持续时间、PID、PPID、退出代码以及进程名称。
9. 最后,使用 bpf_ringbuf_submit 函数将填充好的事件结构体 e 提交到 ring buffer之后在用户空间进行处理和输出。
让我们看看它如何工作。我们首先定义了一个 `BPF_MAP_TYPE_RINGBUF` 类型的 map大小为 256KB。这个环形缓冲区是所有 CPU 共享的,解决了 perf buffer 每个 CPU 单独缓冲区导致的内存浪费问题
这个示例展示了如何使用 exitsnoop 和 ring buffer 在 eBPF 程序中捕获进程退出事件并将相关信息传输到用户空间。这对于分析进程退出原因和监控系统行为非常有用
`handle_exit` 函数中,我们使用了 reserve/submit 模式。首先用 `bpf_ringbuf_reserve` 在 ring buffer 中预留空间——这会返回一个指向预留内存的指针。然后我们直接在这块内存中填充数据:计算进程运行时长(当前时间减去启动时间)、读取 PID、PPID、退出代码和进程名。最后用 `bpf_ringbuf_submit` 提交数据到用户空间
这种模式的优势是避免了额外的内存拷贝,我们直接在 ring buffer 中写入数据而不是先在栈上构建然后再拷贝。注意我们只关心进程退出PID == TID忽略了线程退出事件。
## Compile and Run
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
Compile:
@@ -157,6 +151,6 @@ TIME PID PPID EXIT_CODE DURATION_NS COMM
## 总结
本文介绍了如何使用 eunomia-bpf 开发一个简单的 BPF 程序,该程序可以监控 Linux 系统中的进程退出事件, 并将捕获的事件通过 ring buffer 发送给用户空间程序。在本文中,我们使用 eunomia-bpf 编译运行了这个例子
本文介绍了如何使用 ring buffer 监控 Linux 系统中的进程退出事件。ring buffer 相比 perf buffer 有更好的内存效率和性能,应该作为从内核向用户空间发送数据的首选
为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf> 。此外,我们还为您提供了完整的教程和源代码,您可以在 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或网站 <https://eunomia.dev/zh/tutorials/> 以获取更多示例和完整的教程

View File

@@ -10,15 +10,7 @@ runqlat 是一个 eBPF 工具,用于分析 Linux 系统的调度性能。具
Linux 操作系统使用进程来执行所有的系统和用户任务。这些进程可能被阻塞、杀死、运行,或者正在等待运行。处在后两种状态的进程数量决定了 CPU 运行队列的长度。
进程有几种可能的状态,如
- 可运行或正在运行
- 可中断睡眠
- 不可中断睡眠
- 停止
- 僵尸进程
等待资源或其他函数信号的进程会处在可中断或不可中断的睡眠状态:进程被置入睡眠状态,直到它需要的资源变得可用。然后,根据睡眠的类型,进程可以转移到可运行状态,或者保持睡眠。
进程有几种可能的状态:可运行或正在运行、可中断睡眠、不可中断睡眠、停止,以及僵尸进程。等待资源或其他函数信号的进程会处在可中断或不可中断的睡眠状态:进程被置入睡眠状态,直到它需要的资源变得可用。然后,根据睡眠的类型,进程可以转移到可运行状态,或者保持睡眠。
即使进程拥有它需要的所有资源它也不会立即开始运行。它会转移到可运行状态与其他处在相同状态的进程一起排队。CPU可以在接下来的几秒钟或毫秒内执行这些进程。调度器为 CPU 排列进程,并决定下一个要执行的进程。
@@ -366,7 +358,7 @@ struct hist {
## 编译运行
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子
我们使用 eunomia-bpf 来编译和运行这个示例。你可以从 <https://github.com/eunomia-bpf/eunomia-bpf> 安装它
Compile: