mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-09 21:25:24 +08:00
rename README to chinese documents
This commit is contained in:
@@ -1,41 +1,41 @@
|
||||
# eBPF 入门开发实践教程十一:在 eBPF 中使用 libbpf 开发用户态程序并跟踪 exec() 和 exit() 系统调用
|
||||
# eBPF Tutorial by Example 11: Develop User-Space Programs with libbpf and Trace exec() and exit()
|
||||
|
||||
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
|
||||
eBPF (Extended Berkeley Packet Filter) is a powerful network and performance analysis tool on the Linux kernel. It allows developers to dynamically load, update, and run user-defined code during kernel runtime.
|
||||
|
||||
在本教程中,我们将了解内核态和用户态的 eBPF 程序是如何协同工作的。我们还将学习如何使用原生的 libbpf 开发用户态程序,将 eBPF 应用打包为可执行文件,实现跨内核版本分发。
|
||||
In this tutorial, we will learn how kernel-space and user-space eBPF programs work together. We will also learn how to use the native libbpf to develop user-space programs, package eBPF applications into executable files, and distribute them across different kernel versions.
|
||||
|
||||
## libbpf 库,以及为什么需要使用它
|
||||
## The libbpf Library and Why We Need to Use It
|
||||
|
||||
libbpf 是一个 C 语言库,伴随内核版本分发,用于辅助 eBPF 程序的加载和运行。它提供了用于与 eBPF 系统交互的一组 C API,使开发者能够更轻松地编写用户态程序来加载和管理 eBPF 程序。这些用户态程序通常用于分析、监控或优化系统性能。
|
||||
libbpf is a C language library that is distributed with the kernel version to assist in loading and running eBPF programs. It provides a set of C APIs for interacting with the eBPF system, allowing developers to write user-space programs more easily to load and manage eBPF programs. These user-space programs are typically used for system performance analysis, monitoring, or optimization.
|
||||
|
||||
使用 libbpf 库有以下优势:
|
||||
There are several advantages to using the libbpf library:
|
||||
|
||||
- 它简化了 eBPF 程序的加载、更新和运行过程。
|
||||
- 它提供了一组易于使用的 API,使开发者能够专注于编写核心逻辑,而不是处理底层细节。
|
||||
- 它能够确保与内核中的 eBPF 子系统的兼容性,降低了维护成本。
|
||||
- It simplifies the process of loading, updating, and running eBPF programs.
|
||||
- It provides a set of easy-to-use APIs, allowing developers to focus on writing core logic instead of dealing with low-level details.
|
||||
- It ensures compatibility with the eBPF subsystem in the kernel, reducing maintenance costs.
|
||||
|
||||
同时,libbpf 和 BTF(BPF Type Format)都是 eBPF 生态系统的重要组成部分。它们各自在实现跨内核版本兼容方面发挥着关键作用。BTF(BPF Type Format)是一种元数据格式,用于描述 eBPF 程序中的类型信息。BTF 的主要目的是提供一种结构化的方式,以描述内核中的数据结构,以便 eBPF 程序可以更轻松地访问和操作它们。
|
||||
At the same time, libbpf and BTF (BPF Type Format) are important components of the eBPF ecosystem. They play critical roles in achieving compatibility across different kernel versions. BTF is a metadata format used to describe type information in eBPF programs. The primary purpose of BTF is to provide a structured way to describe data structures in the kernel so that eBPF programs can access and manipulate them more easily.
|
||||
|
||||
BTF 在实现跨内核版本兼容方面的关键作用如下:
|
||||
The key roles of BTF in achieving compatibility across different kernel versions are as follows:
|
||||
|
||||
- BTF 允许 eBPF 程序访问内核数据结构的详细类型信息,而无需对特定内核版本进行硬编码。这使得 eBPF 程序可以适应不同版本的内核,从而实现跨内核版本兼容。
|
||||
- 通过使用 BPF CO-RE(Compile Once, Run Everywhere)技术,eBPF 程序可以利用 BTF 在编译时解析内核数据结构的类型信息,进而生成可以在不同内核版本上运行的 eBPF 程序。
|
||||
- BTF allows eBPF programs to access detailed type information of kernel data structures without hardcoding specific kernel versions. This enables eBPF programs to adapt to different kernel versions, achieving compatibility across kernel versions.
|
||||
- By using BPF CO-RE (Compile Once, Run Everywhere) technology, eBPF programs can leverage BTF to parse the type information of kernel data structures during compilation, thereby generating eBPF programs that can run on different kernel versions.
|
||||
|
||||
结合 libbpf 和 BTF,eBPF 程序可以在各种不同版本的内核上运行,而无需为每个内核版本单独编译。这极大地提高了 eBPF 生态系统的可移植性和兼容性,降低了开发和维护的难度。
|
||||
By combining libbpf and BTF, eBPF programs can run on various kernel versions without the need for separate compilation for each kernel version. This greatly improves the portability and compatibility of the eBPF ecosystem and reduces the difficulty of development and maintenance.
|
||||
|
||||
## 什么是 bootstrap
|
||||
## What is Bootstrap
|
||||
|
||||
Bootstrap 是一个使用 libbpf 的完整应用,它利用 eBPF 程序来跟踪内核中的 exec() 系统调用(通过 SEC("tp/sched/sched_process_exec") handle_exec BPF 程序),这主要对应于新进程的创建(不包括 fork() 部分)。此外,它还跟踪进程的 exit() 系统调用(通过 SEC("tp/sched/sched_process_exit") handle_exit BPF 程序),以了解每个进程何时退出。
|
||||
Bootstrap is a complete application that utilizes libbpf. It uses eBPF programs to trace the exec() system call in the kernel (handled by the SEC("tp/sched/sched_process_exec") handle_exec BPF program), which mainly corresponds to the creation of new processes (excluding the fork() part). In addition, it also traces the exit() system call of processes (handled by the SEC("tp/sched/sched_process_exit") handle_exit BPF program) to understand when each process exits.
|
||||
|
||||
这两个 BPF 程序共同工作,允许捕获关于新进程的有趣信息,例如二进制文件的文件名,以及测量进程的生命周期,并在进程结束时收集有趣的统计信息,例如退出代码或消耗的资源量等。这是深入了解内核内部并观察事物如何真正运作的良好起点。
|
||||
These two BPF programs work together to capture interesting information about new processes, such as the file name of the binary and measure the lifecycle of processes. They also collect interesting statistics, such as exit codes or resource consumption, when a process exits. This is a good starting point to gain a deeper understanding of the inner workings of the kernel and observe how things actually operate.
|
||||
|
||||
Bootstrap 还使用 argp API(libc 的一部分)进行命令行参数解析,使得用户可以通过命令行选项配置应用行为。这种方式提供了灵活性,让用户能够根据实际需求自定义程序行为。虽然这些功能使用 eunomia-bpf 工具也可以实现,但是这里我们使用 libbpf 可以在用户态提供更高的可扩展性,不过也带来了不少额外的复杂度。
|
||||
Bootstrap also uses the argp API (part of libc) for command-line argument parsing, allowing users to configure the behavior of the application through command-line options. This provides flexibility and allows users to customize the program behavior according to their specific needs. While these functionalities can also be achieved using the eunomia-bpf tool, using libbpf here provides higher scalability in user space at the cost of additional complexity.
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Bootstrap 分为两个部分:内核态和用户态。内核态部分是一个 eBPF 程序,它跟踪 exec() 和 exit() 系统调用。用户态部分是一个 C 语言程序,它使用 libbpf 库来加载和运行内核态程序,并处理从内核态程序收集的数据。
|
||||
Bootstrap consists of two parts: kernel space and user space. The kernel space part is an eBPF program that traces the exec() and exit() system calls. The user space part is a C language program that uses the libbpf library to load and run the kernel space program and process the data collected from the kernel space program.
|
||||
|
||||
### 内核态 eBPF 程序 bootstrap.bpf.c
|
||||
### Kernel-space eBPF Program bootstrap.bpf.c
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
@@ -108,7 +108,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
struct event *e;
|
||||
pid_t pid, tid;
|
||||
u64 id, ts, *start_ts, duration_ns = 0;
|
||||
|
||||
|
||||
/* get PID and TID of exiting thread/process */
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = id >> 32;
|
||||
@@ -120,8 +120,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
|
||||
/* if we recorded start of the process, calculate lifetime duration */
|
||||
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
|
||||
if (start_ts)
|
||||
duration_ns = bpf_ktime_get_ns() - *start_ts;
|
||||
if (start_ts)duration_ns = bpf_ktime_get_ns() - *start_ts;
|
||||
else if (min_duration_ns)
|
||||
return 0;
|
||||
bpf_map_delete_elem(&exec_start, &pid);
|
||||
@@ -151,9 +150,9 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
}
|
||||
```
|
||||
|
||||
这段代码是一个内核态 eBPF 程序(bootstrap.bpf.c),主要用于跟踪 exec() 和 exit() 系统调用。它通过 eBPF 程序捕获进程的创建和退出事件,并将相关信息发送到用户态程序进行处理。下面是对代码的详细解释。
|
||||
This code is a kernel-level eBPF program (`bootstrap.bpf.c`) used to trace `exec()` and `exit()` system calls. It captures process creation and exit events using an eBPF program and sends the relevant information to a user-space program for processing. Below is a detailed explanation of the code.
|
||||
|
||||
首先,我们引入所需的头文件,定义 eBPF 程序的许可证以及两个 eBPF maps:exec_start 和 rb。exec_start 是一个哈希类型的 eBPF map,用于存储进程开始执行时的时间戳。rb 是一个环形缓冲区类型的 eBPF map,用于存储捕获的事件数据,并将其发送到用户态程序。
|
||||
First, we include the necessary headers and define the license for the eBPF program. We also define two eBPF maps: `exec_start` and `rb`. `exec_start` is a hash type eBPF map used to store the timestamp when a process starts executing. `rb` is a ring buffer type eBPF map used to store captured event data and send it to the user-space program.
|
||||
|
||||
```c
|
||||
#include "vmlinux.h"
|
||||
@@ -179,7 +178,7 @@ struct {
|
||||
const volatile unsigned long long min_duration_ns = 0;
|
||||
```
|
||||
|
||||
接下来,我们定义了一个名为 handle_exec 的 eBPF 程序,它会在进程执行 exec() 系统调用时触发。首先,我们从当前进程中获取 PID,记录进程开始执行的时间戳,然后将其存储在 exec_start map 中。
|
||||
Next, we define an eBPF program named `handle_exec` which is triggered when a process executes the `exec()` system call. First, we retrieve the PID from the current process, record the timestamp when the process starts executing, and store it in the `exec_start` map.
|
||||
|
||||
```c
|
||||
SEC("tp/sched/sched_process_exec")
|
||||
@@ -194,7 +193,7 @@ int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
|
||||
}
|
||||
```
|
||||
|
||||
然后,我们从环形缓冲区 map rb 中预留一个事件结构,并填充相关数据,如进程 ID、父进程 ID、进程名等。之后,我们将这些数据发送到用户态程序进行处理。
|
||||
Then, we reserve an event structure from the circular buffer map `rb` and fill in the relevant data, such as the process ID, parent process ID, and process name. Afterwards, we send this data to the user-mode program for processing.
|
||||
|
||||
```c
|
||||
// reserve sample from BPF ringbuf
|
||||
@@ -218,7 +217,7 @@ int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
|
||||
return 0;
|
||||
```
|
||||
|
||||
最后,我们定义了一个名为 handle_exit 的 eBPF 程序,它会在进程执行 exit() 系统调用时触发。首先,我们从当前进程中获取 PID 和 TID(线程 ID)。如果 PID 和 TID 不相等,说明这是一个线程退出,我们将忽略此事件。
|
||||
Finally, we define an eBPF program named `handle_exit` that will be triggered when a process executes the `exit()` system call. First, we retrieve the PID and TID (thread ID) from the current process. If the PID and TID are not equal, it means that this is a thread exit, and we will ignore this event.
|
||||
|
||||
```c
|
||||
SEC("tp/sched/sched_process_exit")
|
||||
@@ -237,7 +236,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
}
|
||||
```
|
||||
|
||||
接着,我们查找之前存储在 exec_start map 中的进程开始执行的时间戳。如果找到了时间戳,我们将计算进程的生命周期(持续时间),然后从 exec_start map 中删除该记录。如果未找到时间戳且指定了最小持续时间,则直接返回。
|
||||
Next, we look up the timestamp of when the process started execution, which was previously stored in the `exec_start` map. If a timestamp is found, we calculate the process's lifetime duration and then remove the record from the `exec_start` map. If a timestamp is not found and a minimum duration is specified, we return directly.
|
||||
|
||||
```c
|
||||
// if we recorded start of the process, calculate lifetime duration
|
||||
@@ -253,7 +252,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
return 0;
|
||||
```
|
||||
|
||||
然后,我们从环形缓冲区 map rb 中预留一个事件结构,并填充相关数据,如进程 ID、父进程 ID、进程名、进程持续时间等。最后,我们将这些数据发送到用户态程序进行处理。
|
||||
Then, we reserve an event structure from the circular buffer map `rb` and fill in the relevant data, such as the process ID, parent process ID, process name, and process duration. Finally, we send this data to the user-mode program for processing.
|
||||
|
||||
```c
|
||||
/* reserve sample from BPF ringbuf */
|
||||
@@ -265,21 +264,21 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
task = (struct task_struct *)bpf_get_current_task();
|
||||
|
||||
e->exit_event = true;
|
||||
e->duration_ns = duration_ns;
|
||||
e->pid = pid;
|
||||
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
|
||||
bpf_get_current_comm(&e->comm, sizeof(e->comm));
|
||||
e->duration_ns = duration_ns;```
|
||||
e->pid = pid;
|
||||
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
|
||||
bpf_get_current_comm(&e->comm, sizeof(e->comm));
|
||||
|
||||
/* send data to user-space for post-processing */
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
return 0;
|
||||
/* send data to user-space for post-processing */
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
这样,当进程执行 exec() 或 exit() 系统调用时,我们的 eBPF 程序会捕获相应的事件,并将详细信息发送到用户态程序进行后续处理。这使得我们可以轻松地监控进程的创建和退出,并获取有关进程的详细信息。
|
||||
This way, when a process executes the exec() or exit() system calls, our eBPF program captures the corresponding events and sends detailed information to the user space program for further processing. This allows us to easily monitor process creation and termination and obtain detailed information about the processes.
|
||||
|
||||
除此之外,在 bootstrap.h 中,我们还定义了和用户态交互的数据结构:
|
||||
In addition, in the bootstrap.h file, we also define the data structures for interaction with user space:
|
||||
|
||||
```c
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
@@ -303,7 +302,7 @@ struct event {
|
||||
#endif /* __BOOTSTRAP_H */
|
||||
```
|
||||
|
||||
### 用户态,bootstrap.c
|
||||
### User space, bootstrap.c
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
@@ -347,18 +346,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
||||
case 'd':
|
||||
errno = 0;
|
||||
env.min_duration_ms = strtol(arg, NULL, 10);
|
||||
if (errno || env.min_duration_ms <= 0) {
|
||||
fprintf(stderr, "Invalid duration: %s\n", arg);
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
argp_usage(state);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
if (errno || env.min_duration_ms <= 0) {
|
||||
fprintf(stderr, "Invalid duration: %s\n", arg);
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
argp_usage(state);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct argp argp = {
|
||||
@@ -458,7 +457,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* Process events */
|
||||
printf("%-8s %-5s %-16s %-7s %-7s %s\n",
|
||||
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
|
||||
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
|
||||
while (!exiting) {
|
||||
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
|
||||
/* Ctrl-C will cause -EINTR */
|
||||
@@ -481,9 +480,9 @@ cleanup:
|
||||
}
|
||||
```
|
||||
|
||||
这个用户态程序主要用于加载、验证、附加 eBPF 程序,以及接收 eBPF 程序收集的事件数据,并将其打印出来。我们将分析一些关键部分。
|
||||
This user-level program is mainly used to load, verify, attach eBPF programs, and receive event data collected by eBPF programs and print it out. We will analyze some key parts.
|
||||
|
||||
首先,我们定义了一个 env 结构,用于存储命令行参数:
|
||||
First, we define an env structure to store command line arguments:
|
||||
|
||||
```c
|
||||
static struct env {
|
||||
@@ -492,7 +491,7 @@ static struct env {
|
||||
} env;
|
||||
```
|
||||
|
||||
接下来,我们使用 argp 库来解析命令行参数:
|
||||
Next, we use the argp library to parse command line arguments:
|
||||
|
||||
```c
|
||||
static const struct argp_option opts[] = {
|
||||
@@ -513,17 +512,16 @@ static const struct argp argp = {
|
||||
};
|
||||
```
|
||||
|
||||
main() 函数中,首先解析命令行参数,然后设置 libbpf 的打印回调函数 libbpf_print_fn,以便在需要时输出调试信息:
|
||||
In the main() function, we first parse the command line arguments, and then set the libbpf print callback function libbpf_print_fn to output debug information when needed:
|
||||
|
||||
```c
|
||||
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
libbpf_set_print(libbpf_print_fn);
|
||||
```
|
||||
|
||||
接下来,我们打开 eBPF 脚手架(skeleton)文件,将最小持续时间参数传递给 eBPF 程序,并加载和附加 eBPF 程序:
|
||||
Next, we open the eBPF skeleton file, pass the minimum duration parameter to the eBPF program, and load and attach the eBPF program:
|
||||
|
||||
```c
|
||||
skel = bootstrap_bpf__open();
|
||||
@@ -547,7 +545,7 @@ if (err) {
|
||||
}
|
||||
```
|
||||
|
||||
然后,我们创建一个环形缓冲区(ring buffer),用于接收 eBPF 程序发送的事件数据:
|
||||
Then, we create a ring buffer to receive event data sent by the eBPF program:
|
||||
|
||||
```c
|
||||
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
|
||||
@@ -558,9 +556,9 @@ if (!rb) {
|
||||
}
|
||||
```
|
||||
|
||||
handle_event() 函数会处理从 eBPF 程序收到的事件。根据事件类型(进程执行或退出),它会提取并打印事件信息,如时间戳、进程名、进程 ID、父进程 ID、文件名或退出代码等。
|
||||
The handle_event() function handles events received from the eBPF program. Depending on the event type (process execution or exit), it extracts and prints event information such as timestamp, process name, process ID, parent process ID, file name, or exit code.
|
||||
|
||||
最后,我们使用 ring_buffer__poll() 函数轮询环形缓冲区,处理收到的事件数据:
|
||||
Finally, we use the ring_buffer__poll() function to poll the ring buffer and process the received event data:
|
||||
|
||||
```c
|
||||
while (!exiting) {
|
||||
@@ -569,7 +567,7 @@ while (!exiting) {
|
||||
}
|
||||
```
|
||||
|
||||
当程序收到 SIGINT 或 SIGTERM 信号时,它会最后完成清理、退出操作,关闭和卸载 eBPF 程序:
|
||||
When the program receives the SIGINT or SIGTERM signal, it completes the final cleanup and exit operations, and closes and unloads the eBPF program:
|
||||
|
||||
```c
|
||||
cleanup:
|
||||
@@ -581,25 +579,25 @@ cleanup:
|
||||
}
|
||||
```
|
||||
|
||||
## 安装依赖
|
||||
## Dependency Installation
|
||||
|
||||
构建示例需要 clang、libelf 和 zlib。包名在不同的发行版中可能会有所不同。
|
||||
Building the example requires clang, libelf, and zlib. The package names may vary in different distributions.
|
||||
|
||||
在 Ubuntu/Debian 上,你需要执行以下命令:
|
||||
On Ubuntu/Debian, you need to execute the following command:
|
||||
|
||||
```shell
|
||||
sudo apt install clang libelf1 libelf-dev zlib1g-dev
|
||||
```
|
||||
|
||||
在 CentOS/Fedora 上,你需要执行以下命令:
|
||||
On CentOS/Fedora, you need to execute the following command:
|
||||
|
||||
```shell
|
||||
sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
|
||||
```
|
||||
|
||||
## 编译运行
|
||||
## Compile and Run
|
||||
|
||||
编译运行上述代码:
|
||||
Compile and run the above code:
|
||||
|
||||
```console
|
||||
$ git submodule update --init --recursive
|
||||
@@ -614,15 +612,22 @@ TIME EVENT COMM PID PPID FILENAME/EXIT CODE
|
||||
03:16:41 EXEC sh 110688 80168 /bin/sh
|
||||
03:16:41 EXEC which 110689 110688 /usr/bin/which
|
||||
03:16:41 EXIT which 110689 110688 [0] (0ms)
|
||||
03:16:41 EXIT sh 110688 80168 [0] (0ms)
|
||||
03:16:41 EXEC sh 110690 80168 /bin/sh
|
||||
03:16:41 EXEC ps 110691 110690 /usr/bin/ps
|
||||
03:16:41 EXIT ps 110691 110690 [0] (49ms)
|
||||
03:16:41 EXIT sh 110690 80168 [0] (51ms)
|
||||
03:16:41 EXIT sh 110688 80168 [0] (0ms)".
|
||||
```
|
||||
|
||||
## 总结
|
||||
The complete source code can be found at <https://github.com/eunomia-bpf/bpf-developer-tutorial>
|
||||
|
||||
通过这个实例,我们了解了如何将 eBPF 程序与用户态程序结合使用。这种结合为开发者提供了一个强大的工具集,可以实现跨内核和用户空间的高效数据收集和处理。通过使用 eBPF 和 libbpf,您可以构建更高效、可扩展和安全的监控和性能分析工具。
|
||||
## Summary
|
||||
|
||||
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或网站 <https://eunomia.dev/zh/tutorials/> 以获取更多示例和完整的教程。
|
||||
Through this example, we have learned how to combine eBPF programs with user-space programs. This combination provides developers with a powerful toolkit for efficient data collection and processing across the kernel and user space. By using eBPF and libbpf, you can build more efficient, scalable, and secure monitoring and performance analysis tools.
|
||||
|
||||
In the following tutorials, we will continue to explore the advanced features of eBPF and share more about eBPF development practices. Through continuous learning and practice, you will have a better understanding and mastery of eBPF technology and apply it to solve real-world problems.
|
||||
|
||||
If you would like to learn more about eBPF knowledge and practices, please refer to the official documentation of eunomia-bpf: <https://github.com/eunomia-bpf/eunomia-bpf>. You can also visit our tutorial code repository at <https://github.com/eunomia-bpf/bpf-developer-tutorial> or website <https://eunomia.dev/tutorials/> for more examples and complete tutorials.
|
||||
|
||||
## Reference
|
||||
|
||||
- [Building BPF applications with libbpf-bootstrap](https://nakryiko.com/posts/libbpf-bootstrap/)
|
||||
- <https://github.com/libbpf/libbpf-bootstrap>
|
||||
|
||||
> The original link of this article: <https://eunomia.dev/tutorials/11-bootstrap>
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
# eBPF Tutorial by Example 11: Develop User-Space Programs with libbpf and Trace exec() and exit()
|
||||
# eBPF 入门开发实践教程十一:在 eBPF 中使用 libbpf 开发用户态程序并跟踪 exec() 和 exit() 系统调用
|
||||
|
||||
eBPF (Extended Berkeley Packet Filter) is a powerful network and performance analysis tool on the Linux kernel. It allows developers to dynamically load, update, and run user-defined code during kernel runtime.
|
||||
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
|
||||
|
||||
In this tutorial, we will learn how kernel-space and user-space eBPF programs work together. We will also learn how to use the native libbpf to develop user-space programs, package eBPF applications into executable files, and distribute them across different kernel versions.
|
||||
在本教程中,我们将了解内核态和用户态的 eBPF 程序是如何协同工作的。我们还将学习如何使用原生的 libbpf 开发用户态程序,将 eBPF 应用打包为可执行文件,实现跨内核版本分发。
|
||||
|
||||
## The libbpf Library and Why We Need to Use It
|
||||
## libbpf 库,以及为什么需要使用它
|
||||
|
||||
libbpf is a C language library that is distributed with the kernel version to assist in loading and running eBPF programs. It provides a set of C APIs for interacting with the eBPF system, allowing developers to write user-space programs more easily to load and manage eBPF programs. These user-space programs are typically used for system performance analysis, monitoring, or optimization.
|
||||
libbpf 是一个 C 语言库,伴随内核版本分发,用于辅助 eBPF 程序的加载和运行。它提供了用于与 eBPF 系统交互的一组 C API,使开发者能够更轻松地编写用户态程序来加载和管理 eBPF 程序。这些用户态程序通常用于分析、监控或优化系统性能。
|
||||
|
||||
There are several advantages to using the libbpf library:
|
||||
使用 libbpf 库有以下优势:
|
||||
|
||||
- It simplifies the process of loading, updating, and running eBPF programs.
|
||||
- It provides a set of easy-to-use APIs, allowing developers to focus on writing core logic instead of dealing with low-level details.
|
||||
- It ensures compatibility with the eBPF subsystem in the kernel, reducing maintenance costs.
|
||||
- 它简化了 eBPF 程序的加载、更新和运行过程。
|
||||
- 它提供了一组易于使用的 API,使开发者能够专注于编写核心逻辑,而不是处理底层细节。
|
||||
- 它能够确保与内核中的 eBPF 子系统的兼容性,降低了维护成本。
|
||||
|
||||
At the same time, libbpf and BTF (BPF Type Format) are important components of the eBPF ecosystem. They play critical roles in achieving compatibility across different kernel versions. BTF is a metadata format used to describe type information in eBPF programs. The primary purpose of BTF is to provide a structured way to describe data structures in the kernel so that eBPF programs can access and manipulate them more easily.
|
||||
同时,libbpf 和 BTF(BPF Type Format)都是 eBPF 生态系统的重要组成部分。它们各自在实现跨内核版本兼容方面发挥着关键作用。BTF(BPF Type Format)是一种元数据格式,用于描述 eBPF 程序中的类型信息。BTF 的主要目的是提供一种结构化的方式,以描述内核中的数据结构,以便 eBPF 程序可以更轻松地访问和操作它们。
|
||||
|
||||
The key roles of BTF in achieving compatibility across different kernel versions are as follows:
|
||||
BTF 在实现跨内核版本兼容方面的关键作用如下:
|
||||
|
||||
- BTF allows eBPF programs to access detailed type information of kernel data structures without hardcoding specific kernel versions. This enables eBPF programs to adapt to different kernel versions, achieving compatibility across kernel versions.
|
||||
- By using BPF CO-RE (Compile Once, Run Everywhere) technology, eBPF programs can leverage BTF to parse the type information of kernel data structures during compilation, thereby generating eBPF programs that can run on different kernel versions.
|
||||
- BTF 允许 eBPF 程序访问内核数据结构的详细类型信息,而无需对特定内核版本进行硬编码。这使得 eBPF 程序可以适应不同版本的内核,从而实现跨内核版本兼容。
|
||||
- 通过使用 BPF CO-RE(Compile Once, Run Everywhere)技术,eBPF 程序可以利用 BTF 在编译时解析内核数据结构的类型信息,进而生成可以在不同内核版本上运行的 eBPF 程序。
|
||||
|
||||
By combining libbpf and BTF, eBPF programs can run on various kernel versions without the need for separate compilation for each kernel version. This greatly improves the portability and compatibility of the eBPF ecosystem and reduces the difficulty of development and maintenance.
|
||||
结合 libbpf 和 BTF,eBPF 程序可以在各种不同版本的内核上运行,而无需为每个内核版本单独编译。这极大地提高了 eBPF 生态系统的可移植性和兼容性,降低了开发和维护的难度。
|
||||
|
||||
## What is Bootstrap
|
||||
## 什么是 bootstrap
|
||||
|
||||
Bootstrap is a complete application that utilizes libbpf. It uses eBPF programs to trace the exec() system call in the kernel (handled by the SEC("tp/sched/sched_process_exec") handle_exec BPF program), which mainly corresponds to the creation of new processes (excluding the fork() part). In addition, it also traces the exit() system call of processes (handled by the SEC("tp/sched/sched_process_exit") handle_exit BPF program) to understand when each process exits.
|
||||
Bootstrap 是一个使用 libbpf 的完整应用,它利用 eBPF 程序来跟踪内核中的 exec() 系统调用(通过 SEC("tp/sched/sched_process_exec") handle_exec BPF 程序),这主要对应于新进程的创建(不包括 fork() 部分)。此外,它还跟踪进程的 exit() 系统调用(通过 SEC("tp/sched/sched_process_exit") handle_exit BPF 程序),以了解每个进程何时退出。
|
||||
|
||||
These two BPF programs work together to capture interesting information about new processes, such as the file name of the binary and measure the lifecycle of processes. They also collect interesting statistics, such as exit codes or resource consumption, when a process exits. This is a good starting point to gain a deeper understanding of the inner workings of the kernel and observe how things actually operate.
|
||||
这两个 BPF 程序共同工作,允许捕获关于新进程的有趣信息,例如二进制文件的文件名,以及测量进程的生命周期,并在进程结束时收集有趣的统计信息,例如退出代码或消耗的资源量等。这是深入了解内核内部并观察事物如何真正运作的良好起点。
|
||||
|
||||
Bootstrap also uses the argp API (part of libc) for command-line argument parsing, allowing users to configure the behavior of the application through command-line options. This provides flexibility and allows users to customize the program behavior according to their specific needs. While these functionalities can also be achieved using the eunomia-bpf tool, using libbpf here provides higher scalability in user space at the cost of additional complexity.
|
||||
Bootstrap 还使用 argp API(libc 的一部分)进行命令行参数解析,使得用户可以通过命令行选项配置应用行为。这种方式提供了灵活性,让用户能够根据实际需求自定义程序行为。虽然这些功能使用 eunomia-bpf 工具也可以实现,但是这里我们使用 libbpf 可以在用户态提供更高的可扩展性,不过也带来了不少额外的复杂度。
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Bootstrap consists of two parts: kernel space and user space. The kernel space part is an eBPF program that traces the exec() and exit() system calls. The user space part is a C language program that uses the libbpf library to load and run the kernel space program and process the data collected from the kernel space program.
|
||||
Bootstrap 分为两个部分:内核态和用户态。内核态部分是一个 eBPF 程序,它跟踪 exec() 和 exit() 系统调用。用户态部分是一个 C 语言程序,它使用 libbpf 库来加载和运行内核态程序,并处理从内核态程序收集的数据。
|
||||
|
||||
### Kernel-space eBPF Program bootstrap.bpf.c
|
||||
### 内核态 eBPF 程序 bootstrap.bpf.c
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
@@ -108,7 +108,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
struct event *e;
|
||||
pid_t pid, tid;
|
||||
u64 id, ts, *start_ts, duration_ns = 0;
|
||||
|
||||
|
||||
/* get PID and TID of exiting thread/process */
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = id >> 32;
|
||||
@@ -120,7 +120,8 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
|
||||
/* if we recorded start of the process, calculate lifetime duration */
|
||||
start_ts = bpf_map_lookup_elem(&exec_start, &pid);
|
||||
if (start_ts)duration_ns = bpf_ktime_get_ns() - *start_ts;
|
||||
if (start_ts)
|
||||
duration_ns = bpf_ktime_get_ns() - *start_ts;
|
||||
else if (min_duration_ns)
|
||||
return 0;
|
||||
bpf_map_delete_elem(&exec_start, &pid);
|
||||
@@ -150,9 +151,9 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
}
|
||||
```
|
||||
|
||||
This code is a kernel-level eBPF program (`bootstrap.bpf.c`) used to trace `exec()` and `exit()` system calls. It captures process creation and exit events using an eBPF program and sends the relevant information to a user-space program for processing. Below is a detailed explanation of the code.
|
||||
这段代码是一个内核态 eBPF 程序(bootstrap.bpf.c),主要用于跟踪 exec() 和 exit() 系统调用。它通过 eBPF 程序捕获进程的创建和退出事件,并将相关信息发送到用户态程序进行处理。下面是对代码的详细解释。
|
||||
|
||||
First, we include the necessary headers and define the license for the eBPF program. We also define two eBPF maps: `exec_start` and `rb`. `exec_start` is a hash type eBPF map used to store the timestamp when a process starts executing. `rb` is a ring buffer type eBPF map used to store captured event data and send it to the user-space program.
|
||||
首先,我们引入所需的头文件,定义 eBPF 程序的许可证以及两个 eBPF maps:exec_start 和 rb。exec_start 是一个哈希类型的 eBPF map,用于存储进程开始执行时的时间戳。rb 是一个环形缓冲区类型的 eBPF map,用于存储捕获的事件数据,并将其发送到用户态程序。
|
||||
|
||||
```c
|
||||
#include "vmlinux.h"
|
||||
@@ -178,7 +179,7 @@ struct {
|
||||
const volatile unsigned long long min_duration_ns = 0;
|
||||
```
|
||||
|
||||
Next, we define an eBPF program named `handle_exec` which is triggered when a process executes the `exec()` system call. First, we retrieve the PID from the current process, record the timestamp when the process starts executing, and store it in the `exec_start` map.
|
||||
接下来,我们定义了一个名为 handle_exec 的 eBPF 程序,它会在进程执行 exec() 系统调用时触发。首先,我们从当前进程中获取 PID,记录进程开始执行的时间戳,然后将其存储在 exec_start map 中。
|
||||
|
||||
```c
|
||||
SEC("tp/sched/sched_process_exec")
|
||||
@@ -193,7 +194,7 @@ int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
|
||||
}
|
||||
```
|
||||
|
||||
Then, we reserve an event structure from the circular buffer map `rb` and fill in the relevant data, such as the process ID, parent process ID, and process name. Afterwards, we send this data to the user-mode program for processing.
|
||||
然后,我们从环形缓冲区 map rb 中预留一个事件结构,并填充相关数据,如进程 ID、父进程 ID、进程名等。之后,我们将这些数据发送到用户态程序进行处理。
|
||||
|
||||
```c
|
||||
// reserve sample from BPF ringbuf
|
||||
@@ -217,7 +218,7 @@ Then, we reserve an event structure from the circular buffer map `rb` and fill i
|
||||
return 0;
|
||||
```
|
||||
|
||||
Finally, we define an eBPF program named `handle_exit` that will be triggered when a process executes the `exit()` system call. First, we retrieve the PID and TID (thread ID) from the current process. If the PID and TID are not equal, it means that this is a thread exit, and we will ignore this event.
|
||||
最后,我们定义了一个名为 handle_exit 的 eBPF 程序,它会在进程执行 exit() 系统调用时触发。首先,我们从当前进程中获取 PID 和 TID(线程 ID)。如果 PID 和 TID 不相等,说明这是一个线程退出,我们将忽略此事件。
|
||||
|
||||
```c
|
||||
SEC("tp/sched/sched_process_exit")
|
||||
@@ -236,7 +237,7 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
}
|
||||
```
|
||||
|
||||
Next, we look up the timestamp of when the process started execution, which was previously stored in the `exec_start` map. If a timestamp is found, we calculate the process's lifetime duration and then remove the record from the `exec_start` map. If a timestamp is not found and a minimum duration is specified, we return directly.
|
||||
接着,我们查找之前存储在 exec_start map 中的进程开始执行的时间戳。如果找到了时间戳,我们将计算进程的生命周期(持续时间),然后从 exec_start map 中删除该记录。如果未找到时间戳且指定了最小持续时间,则直接返回。
|
||||
|
||||
```c
|
||||
// if we recorded start of the process, calculate lifetime duration
|
||||
@@ -252,7 +253,7 @@ Next, we look up the timestamp of when the process started execution, which was
|
||||
return 0;
|
||||
```
|
||||
|
||||
Then, we reserve an event structure from the circular buffer map `rb` and fill in the relevant data, such as the process ID, parent process ID, process name, and process duration. Finally, we send this data to the user-mode program for processing.
|
||||
然后,我们从环形缓冲区 map rb 中预留一个事件结构,并填充相关数据,如进程 ID、父进程 ID、进程名、进程持续时间等。最后,我们将这些数据发送到用户态程序进行处理。
|
||||
|
||||
```c
|
||||
/* reserve sample from BPF ringbuf */
|
||||
@@ -264,21 +265,21 @@ Then, we reserve an event structure from the circular buffer map `rb` and fill i
|
||||
task = (struct task_struct *)bpf_get_current_task();
|
||||
|
||||
e->exit_event = true;
|
||||
e->duration_ns = duration_ns;```
|
||||
e->pid = pid;
|
||||
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
|
||||
bpf_get_current_comm(&e->comm, sizeof(e->comm));
|
||||
e->duration_ns = duration_ns;
|
||||
e->pid = pid;
|
||||
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
|
||||
bpf_get_current_comm(&e->comm, sizeof(e->comm));
|
||||
|
||||
/* send data to user-space for post-processing */
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
return 0;
|
||||
/* send data to user-space for post-processing */
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
This way, when a process executes the exec() or exit() system calls, our eBPF program captures the corresponding events and sends detailed information to the user space program for further processing. This allows us to easily monitor process creation and termination and obtain detailed information about the processes.
|
||||
这样,当进程执行 exec() 或 exit() 系统调用时,我们的 eBPF 程序会捕获相应的事件,并将详细信息发送到用户态程序进行后续处理。这使得我们可以轻松地监控进程的创建和退出,并获取有关进程的详细信息。
|
||||
|
||||
In addition, in the bootstrap.h file, we also define the data structures for interaction with user space:
|
||||
除此之外,在 bootstrap.h 中,我们还定义了和用户态交互的数据结构:
|
||||
|
||||
```c
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
@@ -302,7 +303,7 @@ struct event {
|
||||
#endif /* __BOOTSTRAP_H */
|
||||
```
|
||||
|
||||
### User space, bootstrap.c
|
||||
### 用户态,bootstrap.c
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
@@ -346,18 +347,18 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
|
||||
case 'd':
|
||||
errno = 0;
|
||||
env.min_duration_ms = strtol(arg, NULL, 10);
|
||||
if (errno || env.min_duration_ms <= 0) {
|
||||
fprintf(stderr, "Invalid duration: %s\n", arg);
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
argp_usage(state);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
if (errno || env.min_duration_ms <= 0) {
|
||||
fprintf(stderr, "Invalid duration: %s\n", arg);
|
||||
argp_usage(state);
|
||||
}
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
argp_usage(state);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct argp argp = {
|
||||
@@ -457,7 +458,7 @@ int main(int argc, char **argv)
|
||||
|
||||
/* Process events */
|
||||
printf("%-8s %-5s %-16s %-7s %-7s %s\n",
|
||||
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
|
||||
"TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE");
|
||||
while (!exiting) {
|
||||
err = ring_buffer__poll(rb, 100 /* timeout, ms */);
|
||||
/* Ctrl-C will cause -EINTR */
|
||||
@@ -480,9 +481,9 @@ cleanup:
|
||||
}
|
||||
```
|
||||
|
||||
This user-level program is mainly used to load, verify, attach eBPF programs, and receive event data collected by eBPF programs and print it out. We will analyze some key parts.
|
||||
这个用户态程序主要用于加载、验证、附加 eBPF 程序,以及接收 eBPF 程序收集的事件数据,并将其打印出来。我们将分析一些关键部分。
|
||||
|
||||
First, we define an env structure to store command line arguments:
|
||||
首先,我们定义了一个 env 结构,用于存储命令行参数:
|
||||
|
||||
```c
|
||||
static struct env {
|
||||
@@ -491,7 +492,7 @@ static struct env {
|
||||
} env;
|
||||
```
|
||||
|
||||
Next, we use the argp library to parse command line arguments:
|
||||
接下来,我们使用 argp 库来解析命令行参数:
|
||||
|
||||
```c
|
||||
static const struct argp_option opts[] = {
|
||||
@@ -512,16 +513,17 @@ static const struct argp argp = {
|
||||
};
|
||||
```
|
||||
|
||||
In the main() function, we first parse the command line arguments, and then set the libbpf print callback function libbpf_print_fn to output debug information when needed:
|
||||
main() 函数中,首先解析命令行参数,然后设置 libbpf 的打印回调函数 libbpf_print_fn,以便在需要时输出调试信息:
|
||||
|
||||
```c
|
||||
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
libbpf_set_print(libbpf_print_fn);
|
||||
```
|
||||
|
||||
Next, we open the eBPF skeleton file, pass the minimum duration parameter to the eBPF program, and load and attach the eBPF program:
|
||||
接下来,我们打开 eBPF 脚手架(skeleton)文件,将最小持续时间参数传递给 eBPF 程序,并加载和附加 eBPF 程序:
|
||||
|
||||
```c
|
||||
skel = bootstrap_bpf__open();
|
||||
@@ -545,7 +547,7 @@ if (err) {
|
||||
}
|
||||
```
|
||||
|
||||
Then, we create a ring buffer to receive event data sent by the eBPF program:
|
||||
然后,我们创建一个环形缓冲区(ring buffer),用于接收 eBPF 程序发送的事件数据:
|
||||
|
||||
```c
|
||||
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
|
||||
@@ -556,9 +558,9 @@ if (!rb) {
|
||||
}
|
||||
```
|
||||
|
||||
The handle_event() function handles events received from the eBPF program. Depending on the event type (process execution or exit), it extracts and prints event information such as timestamp, process name, process ID, parent process ID, file name, or exit code.
|
||||
handle_event() 函数会处理从 eBPF 程序收到的事件。根据事件类型(进程执行或退出),它会提取并打印事件信息,如时间戳、进程名、进程 ID、父进程 ID、文件名或退出代码等。
|
||||
|
||||
Finally, we use the ring_buffer__poll() function to poll the ring buffer and process the received event data:
|
||||
最后,我们使用 ring_buffer__poll() 函数轮询环形缓冲区,处理收到的事件数据:
|
||||
|
||||
```c
|
||||
while (!exiting) {
|
||||
@@ -567,7 +569,7 @@ while (!exiting) {
|
||||
}
|
||||
```
|
||||
|
||||
When the program receives the SIGINT or SIGTERM signal, it completes the final cleanup and exit operations, and closes and unloads the eBPF program:
|
||||
当程序收到 SIGINT 或 SIGTERM 信号时,它会最后完成清理、退出操作,关闭和卸载 eBPF 程序:
|
||||
|
||||
```c
|
||||
cleanup:
|
||||
@@ -579,25 +581,25 @@ cleanup:
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Installation
|
||||
## 安装依赖
|
||||
|
||||
Building the example requires clang, libelf, and zlib. The package names may vary in different distributions.
|
||||
构建示例需要 clang、libelf 和 zlib。包名在不同的发行版中可能会有所不同。
|
||||
|
||||
On Ubuntu/Debian, you need to execute the following command:
|
||||
在 Ubuntu/Debian 上,你需要执行以下命令:
|
||||
|
||||
```shell
|
||||
sudo apt install clang libelf1 libelf-dev zlib1g-dev
|
||||
```
|
||||
|
||||
On CentOS/Fedora, you need to execute the following command:
|
||||
在 CentOS/Fedora 上,你需要执行以下命令:
|
||||
|
||||
```shell
|
||||
sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel
|
||||
```
|
||||
|
||||
## Compile and Run
|
||||
## 编译运行
|
||||
|
||||
Compile and run the above code:
|
||||
编译运行上述代码:
|
||||
|
||||
```console
|
||||
$ git submodule update --init --recursive
|
||||
@@ -612,22 +614,15 @@ TIME EVENT COMM PID PPID FILENAME/EXIT CODE
|
||||
03:16:41 EXEC sh 110688 80168 /bin/sh
|
||||
03:16:41 EXEC which 110689 110688 /usr/bin/which
|
||||
03:16:41 EXIT which 110689 110688 [0] (0ms)
|
||||
03:16:41 EXIT sh 110688 80168 [0] (0ms)".
|
||||
03:16:41 EXIT sh 110688 80168 [0] (0ms)
|
||||
03:16:41 EXEC sh 110690 80168 /bin/sh
|
||||
03:16:41 EXEC ps 110691 110690 /usr/bin/ps
|
||||
03:16:41 EXIT ps 110691 110690 [0] (49ms)
|
||||
03:16:41 EXIT sh 110690 80168 [0] (51ms)
|
||||
```
|
||||
|
||||
The complete source code can be found at <https://github.com/eunomia-bpf/bpf-developer-tutorial>
|
||||
## 总结
|
||||
|
||||
## Summary
|
||||
通过这个实例,我们了解了如何将 eBPF 程序与用户态程序结合使用。这种结合为开发者提供了一个强大的工具集,可以实现跨内核和用户空间的高效数据收集和处理。通过使用 eBPF 和 libbpf,您可以构建更高效、可扩展和安全的监控和性能分析工具。
|
||||
|
||||
Through this example, we have learned how to combine eBPF programs with user-space programs. This combination provides developers with a powerful toolkit for efficient data collection and processing across the kernel and user space. By using eBPF and libbpf, you can build more efficient, scalable, and secure monitoring and performance analysis tools.
|
||||
|
||||
In the following tutorials, we will continue to explore the advanced features of eBPF and share more about eBPF development practices. Through continuous learning and practice, you will have a better understanding and mastery of eBPF technology and apply it to solve real-world problems.
|
||||
|
||||
If you would like to learn more about eBPF knowledge and practices, please refer to the official documentation of eunomia-bpf: <https://github.com/eunomia-bpf/eunomia-bpf>. You can also visit our tutorial code repository at <https://github.com/eunomia-bpf/bpf-developer-tutorial> or website <https://eunomia.dev/tutorials/> for more examples and complete tutorials.
|
||||
|
||||
## Reference
|
||||
|
||||
- [Building BPF applications with libbpf-bootstrap](https://nakryiko.com/posts/libbpf-bootstrap/)
|
||||
- <https://github.com/libbpf/libbpf-bootstrap>
|
||||
|
||||
> The original link of this article: <https://eunomia.dev/tutorials/11-bootstrap>
|
||||
如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 或网站 <https://eunomia.dev/zh/tutorials/> 以获取更多示例和完整的教程。
|
||||
Reference in New Issue
Block a user