mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-10-31 04:36:26 +08:00
rename README to chinese documents
This commit is contained in:
@@ -1,37 +1,37 @@
|
||||
# eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件
|
||||
# eBPF Tutorial by Example 10: Capturing Interrupts with hardirqs or softirqs
|
||||
|
||||
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 at runtime in the kernel.
|
||||
|
||||
本文是 eBPF 入门开发实践教程的第十篇,在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件。
|
||||
hardirqs 和 softirqs 是 Linux 内核中两种不同类型的中断处理程序。它们用于处理硬件设备产生的中断请求,以及内核中的异步事件。在 eBPF 中,我们可以使用同名的 eBPF 工具 hardirqs 和 softirqs 来捕获和分析内核中与中断处理相关的信息。
|
||||
This article is the tenth part of the eBPF Tutorial by Example, focusing on capturing interrupt events using hardirqs or softirqs in eBPF.
|
||||
hardirqs and softirqs are two different types of interrupt handlers in the Linux kernel. They are used to handle interrupt requests generated by hardware devices, as well as asynchronous events in the kernel. In eBPF, we can use the eBPF tools hardirqs and softirqs to capture and analyze information related to interrupt handling in the kernel.
|
||||
|
||||
## hardirqs 和 softirqs 是什么?
|
||||
## What are hardirqs and softirqs?
|
||||
|
||||
hardirqs 是硬件中断处理程序。当硬件设备产生一个中断请求时,内核会将该请求映射到一个特定的中断向量,然后执行与之关联的硬件中断处理程序。硬件中断处理程序通常用于处理设备驱动程序中的事件,例如设备数据传输完成或设备错误。
|
||||
hardirqs are hardware interrupt handlers. When a hardware device generates an interrupt request, the kernel maps it to a specific interrupt vector and executes the associated hardware interrupt handler. Hardware interrupt handlers are commonly used to handle events in device drivers, such as completion of device data transfer or device errors.
|
||||
|
||||
softirqs 是软件中断处理程序。它们是内核中的一种底层异步事件处理机制,用于处理内核中的高优先级任务。softirqs 通常用于处理网络协议栈、磁盘子系统和其他内核组件中的事件。与硬件中断处理程序相比,软件中断处理程序具有更高的灵活性和可配置性。
|
||||
softirqs are software interrupt handlers. They are a low-level asynchronous event handling mechanism in the kernel, used for handling high-priority tasks in the kernel. softirqs are commonly used to handle events in the network protocol stack, disk subsystem, and other kernel components. Compared to hardware interrupt handlers, software interrupt handlers have more flexibility and configurability.
|
||||
|
||||
## 实现原理
|
||||
## Implementation Details
|
||||
|
||||
在 eBPF 中,我们可以通过挂载特定的 kprobe 或者 tracepoint 来捕获和分析 hardirqs 和 softirqs。为了捕获 hardirqs 和 softirqs,需要在相关的内核函数上放置 eBPF 程序。这些函数包括:
|
||||
In eBPF, we can capture and analyze hardirqs and softirqs by attaching specific kprobes or tracepoints. To capture hardirqs and softirqs, eBPF programs need to be placed on relevant kernel functions. These functions include:
|
||||
|
||||
- 对于 hardirqs:irq_handler_entry 和 irq_handler_exit。
|
||||
- 对于 softirqs:softirq_entry 和 softirq_exit。
|
||||
- For hardirqs: irq_handler_entry and irq_handler_exit.
|
||||
- For softirqs: softirq_entry and softirq_exit.
|
||||
|
||||
当内核处理 hardirqs 或 softirqs 时,这些 eBPF 程序会被执行,从而收集相关信息,如中断向量、中断处理程序的执行时间等。收集到的信息可以用于分析内核中的性能问题和其他与中断处理相关的问题。
|
||||
When the kernel processes hardirqs or softirqs, these eBPF programs are executed to collect relevant information such as interrupt vectors, execution time of interrupt handlers, etc. The collected information can be used for analyzing performance issues and other interrupt handling related problems in the kernel.
|
||||
|
||||
为了捕获 hardirqs 和 softirqs,可以遵循以下步骤:
|
||||
To capture hardirqs and softirqs, the following steps can be followed:
|
||||
|
||||
1. 在 eBPF 程序中定义用于存储中断信息的数据结构和映射。
|
||||
2. 编写 eBPF 程序,将其挂载到相应的内核函数上,以捕获 hardirqs 或 softirqs。
|
||||
3. 在 eBPF 程序中,收集中断处理程序的相关信息,并将这些信息存储在映射中。
|
||||
4. 在用户空间应用程序中,读取映射中的数据以分析和展示中断处理信息。
|
||||
1. Define data structures and maps in eBPF programs for storing interrupt information.
|
||||
2. Write eBPF programs and attach them to the corresponding kernel functions to capture hardirqs or softirqs.
|
||||
3. In eBPF programs, collect relevant information about interrupt handlers and store this information in the maps.
|
||||
4. In user space applications, read the data from the maps to analyze and display the interrupt handling information.
|
||||
|
||||
通过上述方法,我们可以在 eBPF 中使用 hardirqs 和 softirqs 捕获和分析内核中的中断事件,以识别潜在的性能问题和与中断处理相关的问题。
|
||||
By following the above approach, we can use hardirqs and softirqs in eBPF to capture and analyze interrupt events in the kernel, identifying potential performance issues and interrupt handling related problems.
|
||||
|
||||
## hardirqs 代码实现
|
||||
## Implementation of hardirqs Code
|
||||
|
||||
hardirqs 程序的主要目的是获取中断处理程序的名称、执行次数和执行时间,并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码。
|
||||
The main purpose of the hardirqs program is to obtain the name, execution count, and execution time of interrupt handlers and display the distribution of execution time in the form of a histogram. Let's analyze this code step by step.
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
@@ -166,11 +166,11 @@ int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action)
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
```
|
||||
|
||||
这段代码是一个 eBPF 程序,用于捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。程序的主要目的是获取中断处理程序的名称、执行次数和执行时间,并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码。
|
||||
This code is an eBPF program used to capture and analyze the execution information of hardware interrupt handlers (hardirqs) in the kernel. The main purpose of the program is to obtain the name, execution count, and execution time of the interrupt handler, and display the distribution of execution time in the form of a histogram. Let's analyze this code step by step.
|
||||
|
||||
1. 包含必要的头文件和定义数据结构:
|
||||
1. Include necessary header files and define data structures:
|
||||
|
||||
```c
|
||||
```c
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
@@ -178,14 +178,13 @@ char LICENSE[] SEC("license") = "GPL";
|
||||
#include "hardirqs.h"
|
||||
#include "bits.bpf.h"
|
||||
#include "maps.bpf.h"
|
||||
```
|
||||
```
|
||||
|
||||
该程序包含了 eBPF 开发所需的标准头文件,以及用于定义数据结构和映射的自定义头文件。
|
||||
This program includes the standard header files required for eBPF development, as well as custom header files for defining data structures and maps.
|
||||
|
||||
2. 定义全局变量和映射:
|
||||
|
||||
```c
|
||||
2. Define global variables and maps:
|
||||
|
||||
```c
|
||||
#define MAX_ENTRIES 256
|
||||
|
||||
const volatile bool filter_cg = false;
|
||||
@@ -194,18 +193,17 @@ char LICENSE[] SEC("license") = "GPL";
|
||||
const volatile bool do_count = false;
|
||||
|
||||
...
|
||||
```
|
||||
```
|
||||
|
||||
该程序定义了一些全局变量,用于配置程序的行为。例如,`filter_cg` 控制是否过滤 cgroup,`targ_dist` 控制是否显示执行时间的分布等。此外,程序还定义了三个映射,分别用于存储 cgroup 信息、开始时间戳和中断处理程序的信息。
|
||||
This program defines some global variables that are used to configure the behavior of the program. For example, `filter_cg` controls whether to filter cgroups, `targ_dist` controls whether to display the distribution of execution time, etc. Additionally, the program defines three maps for storing cgroup information, start timestamps, and interrupt handler information.
|
||||
|
||||
3. 定义两个辅助函数 `handle_entry` 和 `handle_exit`:
|
||||
3. Define two helper functions `handle_entry` and `handle_exit`:
|
||||
|
||||
这两个函数分别在中断处理程序的入口和出口处被调用。`handle_entry` 记录开始时间戳或更新中断计数,`handle_exit` 计算中断处理程序的执行时间,并将结果存储到相应的信息映射中。
|
||||
These two functions are called at the entry and exit points of the interrupt handler. `handle_entry` records the start timestamp or updates the interrupt count, while `handle_exit` calculates the execution time of the interrupt handler and stores the result in the corresponding information map.
|
||||
|
||||
4. 定义 eBPF 程序的入口点:
|
||||
|
||||
```c
|
||||
4. Define the entry points of the eBPF program:
|
||||
|
||||
```c
|
||||
SEC("tp_btf/irq_handler_entry")
|
||||
int BPF_PROG(irq_handler_entry_btf, int irq, struct irqaction *action)
|
||||
{
|
||||
@@ -229,17 +227,17 @@ char LICENSE[] SEC("license") = "GPL";
|
||||
{
|
||||
return handle_exit(irq, action);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
这里定义了四个 eBPF 程序入口点,分别用于捕获中断处理程序的入口和出口事件。`tp_btf` 和 `raw_tp` 分别代表使用 BPF Type Format(BTF)和原始 tracepoints 捕获事件。这样可以确保程序在不同内核版本上可以移植和运行。
|
||||
Here, four entry points of the eBPF program are defined, which are used to capture the entry and exit events of the interrupt handler. `tp_btf` and `raw_tp` represent capturing events using BPF Type Format (BTF) and raw tracepoints, respectively. This ensures that the program can be ported and run on different kernel versions.
|
||||
|
||||
Softirq 代码也类似,这里就不再赘述了。
|
||||
The code for Softirq is similar, and I won't elaborate on it here.
|
||||
|
||||
## 运行代码
|
||||
## Run code.Translated content
|
||||
|
||||
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines Wasm. Its purpose is to simplify the development, building, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compilation toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
|
||||
要编译这个程序,请使用 ecc 工具:
|
||||
To compile this program, use the ecc tool:
|
||||
|
||||
```console
|
||||
$ ecc hardirqs.bpf.c
|
||||
@@ -247,16 +245,18 @@ Compiling bpf object...
|
||||
Packing ebpf object and config into package.json...
|
||||
```
|
||||
|
||||
然后运行:
|
||||
Then run:
|
||||
|
||||
```console
|
||||
sudo ecli run ./package.json
|
||||
```
|
||||
|
||||
## 总结
|
||||
## Summary
|
||||
|
||||
在本章节(eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件)中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。我们详细讲解了示例代码,包括如何定义数据结构、映射以及 eBPF 程序入口点,以及如何在中断处理程序的入口和出口处调用辅助函数来记录执行信息。
|
||||
In this chapter (eBPF Tutorial by Example Ten: Capturing Interrupt Events in eBPF with Hardirqs or Softirqs), we learned how to capture and analyze the execution information of hardware interrupt handlers (hardirqs) in the kernel using eBPF programs. We explained the example code in detail, including how to define data structures, mappings, eBPF program entry points, and how to call helper functions to record execution information at the entry and exit points of interrupt handlers.
|
||||
|
||||
通过学习本章节内容,您应该已经掌握了如何在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件的方法,以及如何分析这些事件以识别内核中的性能问题和其他与中断处理相关的问题。这些技能对于分析和优化 Linux 内核的性能至关重要。
|
||||
By studying the content of this chapter, you should have mastered the methods of capturing interrupt events with hardirqs or softirqs in eBPF, as well as how to analyze these events to identify performance issues and other problems related to interrupt handling in the kernel. These skills are crucial for analyzing and optimizing the performance of the Linux kernel.
|
||||
|
||||
为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf> 。此外,我们还为您提供了完整的教程和源代码,您可以在 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考。
|
||||
To better understand and practice eBPF programming, we recommend reading the official documentation of eunomia-bpf: <https://github.com/eunomia-bpf/eunomia-bpf>. In addition, we provide a complete tutorial and source code for you to view and learn from at <https://github.com/eunomia-bpf/bpf-developer-tutorial>.
|
||||
|
||||
> The original link of this article: <https://eunomia.dev/tutorials/10-hardirqs/>
|
||||
|
||||
262
src/10-hardirqs/README.zh.md
Normal file
262
src/10-hardirqs/README.zh.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件
|
||||
|
||||
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
|
||||
|
||||
本文是 eBPF 入门开发实践教程的第十篇,在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件。
|
||||
hardirqs 和 softirqs 是 Linux 内核中两种不同类型的中断处理程序。它们用于处理硬件设备产生的中断请求,以及内核中的异步事件。在 eBPF 中,我们可以使用同名的 eBPF 工具 hardirqs 和 softirqs 来捕获和分析内核中与中断处理相关的信息。
|
||||
|
||||
## hardirqs 和 softirqs 是什么?
|
||||
|
||||
hardirqs 是硬件中断处理程序。当硬件设备产生一个中断请求时,内核会将该请求映射到一个特定的中断向量,然后执行与之关联的硬件中断处理程序。硬件中断处理程序通常用于处理设备驱动程序中的事件,例如设备数据传输完成或设备错误。
|
||||
|
||||
softirqs 是软件中断处理程序。它们是内核中的一种底层异步事件处理机制,用于处理内核中的高优先级任务。softirqs 通常用于处理网络协议栈、磁盘子系统和其他内核组件中的事件。与硬件中断处理程序相比,软件中断处理程序具有更高的灵活性和可配置性。
|
||||
|
||||
## 实现原理
|
||||
|
||||
在 eBPF 中,我们可以通过挂载特定的 kprobe 或者 tracepoint 来捕获和分析 hardirqs 和 softirqs。为了捕获 hardirqs 和 softirqs,需要在相关的内核函数上放置 eBPF 程序。这些函数包括:
|
||||
|
||||
- 对于 hardirqs:irq_handler_entry 和 irq_handler_exit。
|
||||
- 对于 softirqs:softirq_entry 和 softirq_exit。
|
||||
|
||||
当内核处理 hardirqs 或 softirqs 时,这些 eBPF 程序会被执行,从而收集相关信息,如中断向量、中断处理程序的执行时间等。收集到的信息可以用于分析内核中的性能问题和其他与中断处理相关的问题。
|
||||
|
||||
为了捕获 hardirqs 和 softirqs,可以遵循以下步骤:
|
||||
|
||||
1. 在 eBPF 程序中定义用于存储中断信息的数据结构和映射。
|
||||
2. 编写 eBPF 程序,将其挂载到相应的内核函数上,以捕获 hardirqs 或 softirqs。
|
||||
3. 在 eBPF 程序中,收集中断处理程序的相关信息,并将这些信息存储在映射中。
|
||||
4. 在用户空间应用程序中,读取映射中的数据以分析和展示中断处理信息。
|
||||
|
||||
通过上述方法,我们可以在 eBPF 中使用 hardirqs 和 softirqs 捕获和分析内核中的中断事件,以识别潜在的性能问题和与中断处理相关的问题。
|
||||
|
||||
## hardirqs 代码实现
|
||||
|
||||
hardirqs 程序的主要目的是获取中断处理程序的名称、执行次数和执行时间,并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码。
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Wenbo Zhang
|
||||
#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"
|
||||
|
||||
#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;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
|
||||
__type(key, u32);
|
||||
__type(value, u32);
|
||||
__uint(max_entries, 1);
|
||||
} cgroup_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, u32);
|
||||
__type(value, u64);
|
||||
} start SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, MAX_ENTRIES);
|
||||
__type(key, struct irq_key);
|
||||
__type(value, struct info);
|
||||
} infos SEC(".maps");
|
||||
|
||||
static struct info zero;
|
||||
|
||||
static int handle_entry(int irq, struct irqaction *action)
|
||||
{
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
if (do_count) {
|
||||
struct irq_key key = {};
|
||||
struct info *info;
|
||||
|
||||
bpf_probe_read_kernel_str(&key.name, sizeof(key.name), BPF_CORE_READ(action, name));
|
||||
info = bpf_map_lookup_or_try_init(&infos, &key, &zero);
|
||||
if (!info)
|
||||
return 0;
|
||||
info->count += 1;
|
||||
return 0;
|
||||
} else {
|
||||
u64 ts = bpf_ktime_get_ns();
|
||||
u32 key = 0;
|
||||
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
bpf_map_update_elem(&start, &key, &ts, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_exit(int irq, struct irqaction *action)
|
||||
{
|
||||
struct irq_key ikey = {};
|
||||
struct info *info;
|
||||
u32 key = 0;
|
||||
u64 delta;
|
||||
u64 *tsp;
|
||||
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
tsp = bpf_map_lookup_elem(&start, &key);
|
||||
if (!tsp)
|
||||
return 0;
|
||||
|
||||
delta = bpf_ktime_get_ns() - *tsp;
|
||||
if (!targ_ns)
|
||||
delta /= 1000U;
|
||||
|
||||
bpf_probe_read_kernel_str(&ikey.name, sizeof(ikey.name), BPF_CORE_READ(action, name));
|
||||
info = bpf_map_lookup_or_try_init(&infos, &ikey, &zero);
|
||||
if (!info)
|
||||
return 0;
|
||||
|
||||
if (!targ_dist) {
|
||||
info->count += delta;
|
||||
} else {
|
||||
u64 slot;
|
||||
|
||||
slot = log2(delta);
|
||||
if (slot >= MAX_SLOTS)
|
||||
slot = MAX_SLOTS - 1;
|
||||
info->slots[slot]++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
```
|
||||
|
||||
这段代码是一个 eBPF 程序,用于捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。程序的主要目的是获取中断处理程序的名称、执行次数和执行时间,并以直方图的形式展示执行时间的分布。让我们一步步分析这段代码。
|
||||
|
||||
1. 包含必要的头文件和定义数据结构:
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
该程序包含了 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 Format(BTF)和原始 tracepoints 捕获事件。这样可以确保程序在不同内核版本上可以移植和运行。
|
||||
|
||||
Softirq 代码也类似,这里就不再赘述了。
|
||||
|
||||
## 运行代码
|
||||
|
||||
eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 <https://github.com/eunomia-bpf/eunomia-bpf> 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。
|
||||
|
||||
要编译这个程序,请使用 ecc 工具:
|
||||
|
||||
```console
|
||||
$ ecc hardirqs.bpf.c
|
||||
Compiling bpf object...
|
||||
Packing ebpf object and config into package.json...
|
||||
```
|
||||
|
||||
然后运行:
|
||||
|
||||
```console
|
||||
sudo ecli run ./package.json
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
在本章节(eBPF 入门开发实践教程十:在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件)中,我们学习了如何使用 eBPF 程序捕获和分析内核中硬件中断处理程序(hardirqs)的执行信息。我们详细讲解了示例代码,包括如何定义数据结构、映射以及 eBPF 程序入口点,以及如何在中断处理程序的入口和出口处调用辅助函数来记录执行信息。
|
||||
|
||||
通过学习本章节内容,您应该已经掌握了如何在 eBPF 中使用 hardirqs 或 softirqs 捕获中断事件的方法,以及如何分析这些事件以识别内核中的性能问题和其他与中断处理相关的问题。这些技能对于分析和优化 Linux 内核的性能至关重要。
|
||||
|
||||
为了更好地理解和实践 eBPF 编程,我们建议您阅读 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf> 。此外,我们还为您提供了完整的教程和源代码,您可以在 <https://github.com/eunomia-bpf/bpf-developer-tutorial> 中查看和学习。希望本教程能够帮助您顺利入门 eBPF 开发,并为您的进一步学习和实践提供有益的参考。
|
||||
@@ -1,262 +0,0 @@
|
||||
# eBPF Tutorial by Example 10: Capturing Interrupts with hardirqs or softirqs
|
||||
|
||||
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 at runtime in the kernel.
|
||||
|
||||
This article is the tenth part of the eBPF Tutorial by Example, focusing on capturing interrupt events using hardirqs or softirqs in eBPF.
|
||||
hardirqs and softirqs are two different types of interrupt handlers in the Linux kernel. They are used to handle interrupt requests generated by hardware devices, as well as asynchronous events in the kernel. In eBPF, we can use the eBPF tools hardirqs and softirqs to capture and analyze information related to interrupt handling in the kernel.
|
||||
|
||||
## What are hardirqs and softirqs?
|
||||
|
||||
hardirqs are hardware interrupt handlers. When a hardware device generates an interrupt request, the kernel maps it to a specific interrupt vector and executes the associated hardware interrupt handler. Hardware interrupt handlers are commonly used to handle events in device drivers, such as completion of device data transfer or device errors.
|
||||
|
||||
softirqs are software interrupt handlers. They are a low-level asynchronous event handling mechanism in the kernel, used for handling high-priority tasks in the kernel. softirqs are commonly used to handle events in the network protocol stack, disk subsystem, and other kernel components. Compared to hardware interrupt handlers, software interrupt handlers have more flexibility and configurability.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
In eBPF, we can capture and analyze hardirqs and softirqs by attaching specific kprobes or tracepoints. To capture hardirqs and softirqs, eBPF programs need to be placed on relevant kernel functions. These functions include:
|
||||
|
||||
- For hardirqs: irq_handler_entry and irq_handler_exit.
|
||||
- For softirqs: softirq_entry and softirq_exit.
|
||||
|
||||
When the kernel processes hardirqs or softirqs, these eBPF programs are executed to collect relevant information such as interrupt vectors, execution time of interrupt handlers, etc. The collected information can be used for analyzing performance issues and other interrupt handling related problems in the kernel.
|
||||
|
||||
To capture hardirqs and softirqs, the following steps can be followed:
|
||||
|
||||
1. Define data structures and maps in eBPF programs for storing interrupt information.
|
||||
2. Write eBPF programs and attach them to the corresponding kernel functions to capture hardirqs or softirqs.
|
||||
3. In eBPF programs, collect relevant information about interrupt handlers and store this information in the maps.
|
||||
4. In user space applications, read the data from the maps to analyze and display the interrupt handling information.
|
||||
|
||||
By following the above approach, we can use hardirqs and softirqs in eBPF to capture and analyze interrupt events in the kernel, identifying potential performance issues and interrupt handling related problems.
|
||||
|
||||
## Implementation of hardirqs Code
|
||||
|
||||
The main purpose of the hardirqs program is to obtain the name, execution count, and execution time of interrupt handlers and display the distribution of execution time in the form of a histogram. Let's analyze this code step by step.
|
||||
|
||||
```c
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2020 Wenbo Zhang
|
||||
#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"
|
||||
|
||||
#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;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
|
||||
__type(key, u32);
|
||||
__type(value, u32);
|
||||
__uint(max_entries, 1);
|
||||
} cgroup_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, u32);
|
||||
__type(value, u64);
|
||||
} start SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, MAX_ENTRIES);
|
||||
__type(key, struct irq_key);
|
||||
__type(value, struct info);
|
||||
} infos SEC(".maps");
|
||||
|
||||
static struct info zero;
|
||||
|
||||
static int handle_entry(int irq, struct irqaction *action)
|
||||
{
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
if (do_count) {
|
||||
struct irq_key key = {};
|
||||
struct info *info;
|
||||
|
||||
bpf_probe_read_kernel_str(&key.name, sizeof(key.name), BPF_CORE_READ(action, name));
|
||||
info = bpf_map_lookup_or_try_init(&infos, &key, &zero);
|
||||
if (!info)
|
||||
return 0;
|
||||
info->count += 1;
|
||||
return 0;
|
||||
} else {
|
||||
u64 ts = bpf_ktime_get_ns();
|
||||
u32 key = 0;
|
||||
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
bpf_map_update_elem(&start, &key, &ts, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_exit(int irq, struct irqaction *action)
|
||||
{
|
||||
struct irq_key ikey = {};
|
||||
struct info *info;
|
||||
u32 key = 0;
|
||||
u64 delta;
|
||||
u64 *tsp;
|
||||
|
||||
if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
tsp = bpf_map_lookup_elem(&start, &key);
|
||||
if (!tsp)
|
||||
return 0;
|
||||
|
||||
delta = bpf_ktime_get_ns() - *tsp;
|
||||
if (!targ_ns)
|
||||
delta /= 1000U;
|
||||
|
||||
bpf_probe_read_kernel_str(&ikey.name, sizeof(ikey.name), BPF_CORE_READ(action, name));
|
||||
info = bpf_map_lookup_or_try_init(&infos, &ikey, &zero);
|
||||
if (!info)
|
||||
return 0;
|
||||
|
||||
if (!targ_dist) {
|
||||
info->count += delta;
|
||||
} else {
|
||||
u64 slot;
|
||||
|
||||
slot = log2(delta);
|
||||
if (slot >= MAX_SLOTS)
|
||||
slot = MAX_SLOTS - 1;
|
||||
info->slots[slot]++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
```
|
||||
|
||||
This code is an eBPF program used to capture and analyze the execution information of hardware interrupt handlers (hardirqs) in the kernel. The main purpose of the program is to obtain the name, execution count, and execution time of the interrupt handler, and display the distribution of execution time in the form of a histogram. Let's analyze this code step by step.
|
||||
|
||||
1. Include necessary header files and define data structures:
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
This program includes the standard header files required for eBPF development, as well as custom header files for defining data structures and maps.
|
||||
|
||||
2. Define global variables and maps:
|
||||
|
||||
```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;
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
This program defines some global variables that are used to configure the behavior of the program. For example, `filter_cg` controls whether to filter cgroups, `targ_dist` controls whether to display the distribution of execution time, etc. Additionally, the program defines three maps for storing cgroup information, start timestamps, and interrupt handler information.
|
||||
|
||||
3. Define two helper functions `handle_entry` and `handle_exit`:
|
||||
|
||||
These two functions are called at the entry and exit points of the interrupt handler. `handle_entry` records the start timestamp or updates the interrupt count, while `handle_exit` calculates the execution time of the interrupt handler and stores the result in the corresponding information map.
|
||||
|
||||
4. Define the entry points of the eBPF program:
|
||||
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
||||
Here, four entry points of the eBPF program are defined, which are used to capture the entry and exit events of the interrupt handler. `tp_btf` and `raw_tp` represent capturing events using BPF Type Format (BTF) and raw tracepoints, respectively. This ensures that the program can be ported and run on different kernel versions.
|
||||
|
||||
The code for Softirq is similar, and I won't elaborate on it here.
|
||||
|
||||
## Run code.Translated content
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines Wasm. Its purpose is to simplify the development, building, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compilation toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
|
||||
To compile this program, use the ecc tool:
|
||||
|
||||
```console
|
||||
$ ecc hardirqs.bpf.c
|
||||
Compiling bpf object...
|
||||
Packing ebpf object and config into package.json...
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```console
|
||||
sudo ecli run ./package.json
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
In this chapter (eBPF Tutorial by Example Ten: Capturing Interrupt Events in eBPF with Hardirqs or Softirqs), we learned how to capture and analyze the execution information of hardware interrupt handlers (hardirqs) in the kernel using eBPF programs. We explained the example code in detail, including how to define data structures, mappings, eBPF program entry points, and how to call helper functions to record execution information at the entry and exit points of interrupt handlers.
|
||||
|
||||
By studying the content of this chapter, you should have mastered the methods of capturing interrupt events with hardirqs or softirqs in eBPF, as well as how to analyze these events to identify performance issues and other problems related to interrupt handling in the kernel. These skills are crucial for analyzing and optimizing the performance of the Linux kernel.
|
||||
|
||||
To better understand and practice eBPF programming, we recommend reading the official documentation of eunomia-bpf: <https://github.com/eunomia-bpf/eunomia-bpf>. In addition, we provide a complete tutorial and source code for you to view and learn from at <https://github.com/eunomia-bpf/bpf-developer-tutorial>.
|
||||
|
||||
> The original link of this article: <https://eunomia.dev/tutorials/10-hardirqs/>
|
||||
Reference in New Issue
Block a user