目录
代码
BPF程序部分
功能说明
头文件引入说明
my_pid_map SEC(".maps") 介绍
bpf_map_lookup_elem
用户程序部分
功能说明
bpf_map__update_elem
执行效果
/* 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 系统调用
这是 BPF map,是 eBPF 程序和用户空间程序交互的主要方式。BPF map 是一种在内核中存储数据的数据结构,它可以被加载的 BPF 程序和用户空间的应用程序共享。
以下是一些常见的 BPF map 类型:
以下是一些常见的 ELF section:
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 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 中的一个元素时,需要提供一个标志来指定更新操作的行为。这个标志可以是以下几个值之一:
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.