libbpf-bootstrap开发指南:使用map结构进行参数传递 - minimal_legacy

目录

代码

BPF程序部分

功能说明

头文件引入说明

my_pid_map SEC(".maps") 介绍

bpf_map_lookup_elem

用户程序部分

功能说明

bpf_map__update_elem

执行效果


代码

BPF程序部分

/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#define BPF_NO_GLOBAL_DATA
#include 
#include 
#include 

typedef unsigned int u32;
typedef int pid_t;

char LICENSE[] SEC("license") = "Dual BSD/GPL";

/* Create an array with 1 entry instead of a global variable
 * which does not work with older kernels */
struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__uint(max_entries, 1);
	__type(key, u32);
	__type(value, pid_t);
} my_pid_map SEC(".maps");

SEC("tp/syscalls/sys_enter_write")
int handle_tp(void *ctx)
{
	u32 index = 0;
	pid_t pid = bpf_get_current_pid_tgid() >> 32;
	pid_t *my_pid = bpf_map_lookup_elem(&my_pid_map, &index);

	if (!my_pid || *my_pid != pid)
		return 1;

	bpf_printk("BPF triggered from PID %d.\n", pid);

	return 0;
}
功能说明

与minimal 一致,在系统调用 write 开始时,检查当前进程的 PID 是否与 my_pid_map 中存储的 PID 匹配,如果匹配就打印一条信息。这可以用于追踪特定进程的 write 系统调用

头文件引入说明
  1. #include 这个头文件定义了 BPF 程序中的核心数据类型和相关的宏。这些定义包括 BPF 程序类型(例如 BPF_PROG_TYPE_XDP、BPF_PROG_TYPE_SOCKET_FILTER 等)、BPF map 类型(例如 BPF_MAP_TYPE_HASH、BPF_MAP_TYPE_ARRAY 等)以及一些函数原型等。在代码中,__uint(type, BPF_MAP_TYPE_ARRAY); 和 __uint(max_entries, 1); 这两行代码使用了 中定义的 __uint 宏和 BPF_MAP_TYPE_ARRAY 类型。
  2. #include 这个头文件定义了一组辅助函数,这些函数可以在 BPF 程序中使用,以便与内核进行交互。这些辅助函数包括用于操作 BPF maps 的函数(例如 bpf_map_lookup_elem、bpf_map_update_elem 等)、获取当前上下文信息的函数(例如 bpf_get_current_pid_tgid 等)以及一些其他功能的函数。在代码中,bpf_get_current_pid_tgid() 和 bpf_map_lookup_elem(&my_pid_map, &index); 这两行代码使用了 中定义的函数。
  3. #include 这个头文件包含了在 BPF 程序中使用的跟踪点(Tracepoints)相关的定义和宏。Tracepoints 可以用于在内核中的特定位置插入钩子,从而收集系统的运行信息。在代码中,SEC("tp/syscalls/sys_enter_write") 这一行使用了 中定义的 SEC 宏,该宏用于指定一个 Tracepoint 的位置。
my_pid_map SEC(".maps") 介绍

这是 BPF map,是 eBPF 程序和用户空间程序交互的主要方式。BPF map 是一种在内核中存储数据的数据结构,它可以被加载的 BPF 程序和用户空间的应用程序共享。

  • __uint(type, BPF_MAP_TYPE_ARRAY); 这行定义了 BPF map 的类型为 BPF_MAP_TYPE_ARRAY。这是一个简单的数组类型的 map,基于索引来存储和查找元素。
  • __uint(max_entries, 1); 这行定义了 BPF map 的最大条目数,也就是说,这个 map 可以存储的元素数量最多为 1。
  • __type(key, u32); 这行定义了 BPF map 的键的类型为 u32(无符号的 32 位整数)。
  • __type(value, pid_t); 这行定义了 BPF map 的值的类型为 pid_t(在这个程序中定义为 int,通常用来表示进程 ID)。
  • my_pid_map SEC(".maps"); 这行定义了这个 BPF map 的名称为 my_pid_map,并且指定了这个 map 存储在 ".maps" 的 ELF section 中,这样 BPF loader 才能找到并加载这个 map。

以下是一些常见的 BPF map 类型:

  1. BPF_MAP_TYPE_HASH:这是一个基于哈希表的 map,可以使用任意类型的键进行访问。
  2. BPF_MAP_TYPE_ARRAY:这是一个基于数组的 map,使用无符号 32 位整数作为键进行访问。
  3. BPF_MAP_TYPE_PROG_ARRAY:这是一种特别的数组 map,它的值是其他 BPF 程序的文件描述符。这种 map 可以用于实现 BPF 程序的跳转。
  4. BPF_MAP_TYPE_PERF_EVENT_ARRAY:这种 map 用于存储 perf_event 文件描述符,常常用于与 perf 工具集成。
  5. BPF_MAP_TYPE_PERCPU_HASH:这是一个每 CPU 的哈希表 map,每个 CPU 都有自己的独立的哈希表。
  6. BPF_MAP_TYPE_PERCPU_ARRAY:这是一个每 CPU 的数组 map,每个 CPU 都有自己的独立的数组。
  7. BPF_MAP_TYPE_STACK_TRACE:这种 map 用于存储堆栈跟踪的结果。
  8. BPF_MAP_TYPE_CGROUP_ARRAY:这种 map 用于存储 cgroup 文件描述符,可以用于 cgroup 级别的 BPF 程序。
  9. BPF_MAP_TYPE_LRU_HASH:这是一个 LRU(最近最少使用)策略的哈希表 map。
  10. BPF_MAP_TYPE_LRU_PERCPU_HASH:这是一个每 CPU 的 LRU 策略的哈希表 map。

以下是一些常见的 ELF section:

  1. .text:这个 section 通常包含程序的机器代码。
  2. .rodata:这个 section 通常包含只读数据,例如字符串常量和其他常量。
  3. .bss:这个 section 通常包含未初始化的全局变量。在程序开始执行前,内核会把这个区域清零。
  4. .data:这个 section 通常包含已初始化的全局变量。
  5. .maps:这个 section 用于存放 BPF map 的定义。
  6. .license:这个 section 包含一个字符串,表示加载 BPF 程序时所需的许可证。
bpf_map_lookup_elem
void *bpf_map_lookup_elem(struct bpf_map *map, void *key);

作用是在 BPF map 中查找一个元素。在这个例子中,BPF map 的名字是 my_pid_map,而要查找的元素的键是 index(也就是0)。

这个函数接收两个参数:一个是 map 的引用,另一个是要查找的元素的键。如果找到了对应的元素,这个函数会返回指向该元素的指针;如果没有找到,或者发生了错误,这个函数会返回 NULL。

用户程序部分

/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#include 
#include 
#include 
#include 
#include "minimal_legacy.skel.h"

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
	return vfprintf(stderr, format, args);
}

int main(int argc, char **argv)
{
	struct minimal_legacy_bpf *skel;
	int err;
	pid_t pid;
	unsigned index = 0;

	/* Set up libbpf errors and debug info callback */
	libbpf_set_print(libbpf_print_fn);

	/* Load and verify BPF application */
	skel = minimal_legacy_bpf__open_and_load();
	if (!skel) {
		fprintf(stderr, "Failed to open and load BPF skeleton\n");
		return 1;
	}

	/* ensure BPF program only handles write() syscalls from our process */
	pid = getpid();
	err = bpf_map__update_elem(skel->maps.my_pid_map, &index, sizeof(index), &pid,
				   sizeof(pid_t), BPF_ANY);
	if (err < 0) {
		fprintf(stderr, "Error updating map with pid: %s\n", strerror(err));
		goto cleanup;
	}

	/* Attach tracepoint handler */
	err = minimal_legacy_bpf__attach(skel);
	if (err) {
		fprintf(stderr, "Failed to attach BPF skeleton\n");
		goto cleanup;
	}

	printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
	       "to see output of the BPF programs.\n");

	for (;;) {
		/* trigger our BPF program */
		fprintf(stderr, ".");
		sleep(1);
	}

cleanup:
	minimal_legacy_bpf__destroy(skel);
	return -err;
}
功能说明

功能与minimal 是一致的,不同的是pid 的传入方式改为了使用一个 bpf程序和用户态程序共享的map 数组

err = bpf_map__update_elem(skel->maps.my_pid_map, &index, sizeof(index), &pid,
				   sizeof(pid_t), BPF_ANY);
bpf_map__update_elem

bpf_map__update_elem 是一个 BPF helper 函数,用于更新 BPF map 中的一个元素。这个函数接收三个参数:map 的文件描述符、要更新的元素的键和新的值。

int bpf_map_update_elem(int fd, const void *key, const void *value, __u64 flags);

其中,fd 是 map 的文件描述符,key 是指向要更新的元素的键的指针,value 是指向新的值的指针,flags 是用于指定更新操作的行为的标志。在大多数情况下,flags 的值应该设为 BPF_ANY,这意味着如果 map 中已经存在键为 key 的元素,那么就更新这个元素的值,否则就插入一个新的元素。

函数的返回值是一个整数。如果更新成功,返回值为 0。如果更新失败,返回值为一个负的错误码。

当你调用 bpf_map_update_elem 更新 map 中的一个元素时,需要提供一个标志来指定更新操作的行为。这个标志可以是以下几个值之一:

  • BPF_ANY:无论 key 是否存在,都插入或更新 map 中的元素。如果 key 已经存在,那么就更新相应的元素。如果 key 不存在,那么就创建一个新的元素。
  • BPF_NOEXIST:只有当 key 不存在时,才插入一个新的元素。如果 key 已经存在,那么操作会失败。
  • BPF_EXIST:只有当 key 已经存在时,才更新相应的元素。如果 key 不存在,那么操作会失败。

执行效果

 minimal_legacy-29334   [012] d..31 22642.892062: bpf_trace_printk: BPF triggered from PID 29334.

  minimal_legacy-29334   [012] d..31 22643.892624: bpf_trace_printk: BPF triggered from PID 29334.

  minimal_legacy-29334   [012] d..31 22644.893188: bpf_trace_printk: BPF triggered from PID 29334.

  minimal_legacy-29334   [012] d..31 22645.893750: bpf_trace_printk: BPF triggered from PID 29334.

你可能感兴趣的:(BPF,bootstrap,前端,html,性能优化)