网站建设的实验结论网站推广协议
news/
2025/10/9 11:29:06/
文章来源:
网站建设的实验结论,网站推广协议,西安网站建设小程序开发,龙华区做网站介绍eBPF技术
当代计算机系统中#xff0c;性能、安全性和可观察性是至关重要的关键因素。为了应对这些挑战#xff0c;Linux 内核引入了一种名为eBPF#xff08;extended Berkeley Packet Filter#xff09;的强大技术。eBPF 不仅仅是一种网络数据包过滤器#xff0c;它…介绍eBPF技术
当代计算机系统中性能、安全性和可观察性是至关重要的关键因素。为了应对这些挑战Linux 内核引入了一种名为eBPFextended Berkeley Packet Filter的强大技术。eBPF 不仅仅是一种网络数据包过滤器它是一种可编程的内核技术可以用于解决各种各样的问题从网络性能优化到安全审计。本文将深入介绍eBPF技术包括其起源、基本概念、工作原理、应用领域和相关工具。
在当今计算机领域Linux 内核是一个广泛使用的操作系统内核而eBPF是 Linux 内核中的一项创新技术它具有广泛的用途可以用于改善系统性能、提高安全性、监控和审计系统行为。eBPF 能够实现高性能和可编程性使其成为解决多种问题的理想选择。
eBPF的起源
eBPF 最初起源于 Berkeley Packet FilterBPF后来演化为 extended BPF随着时间的推移它已经成为 Linux 内核的一部分。BPF 最早是由 Steven McCanne 和 Van Jacobson 在 1992 年创建的用于数据包捕获和过滤。extended BPF 允许用户编写更复杂的程序而不仅仅是数据包过滤从而扩展了其应用范围。
eBPF的基本概念
eBPF 提供了一个基于寄存器的虚拟机可以执行内核中嵌入的程序这些程序通常称为 BPF 程序。这些程序使用一种特殊的指令集可以访问内核数据和功能并在内核中执行。eBPF 具有以下基本概念
BPF 程序eBPF 程序是由用户编写的程序使用 BPF 指令集执行。它们通常编译成字节码并加载到内核中。
BPF 虚拟机eBPF 程序在 BPF 虚拟机中执行这是一个安全且受限的执行环境可以在内核中运行用户定义的代码。
BPF 地址空间eBPF 程序可以访问内核的地址空间包括数据结构和函数。
Hook PointseBPF 可以绑定到内核的挂钩点hook points这些挂钩点允许程序拦截和修改内核事件。
eBPF的工作原理
eBPF 工作原理涉及以下步骤
1、用户编写 eBPF 程序通常使用高级语言如C编写并使用 BPF 编译器编译成字节码。
2、编译后的程序加载到内核中然后通过 BPF 虚拟机执行。程序可以连接到内核的挂钩点hook points例如网络数据包处理、系统调用等。
3、在运行时eBPF 程序可以访问内核数据和事件并根据需求执行自定义逻辑。
4、eBPF 程序可以生成输出或者修改内核状态以实现各种用途例如性能优化、安全审计、监控和跟踪。
这里有详细的文档https://man7.org/linux/man-pages/man7/bpf-helpers.7.html
eBPF的应用领域
eBPF 具有多个应用领域包括但不限于
网络性能优化
eBPF 可以用于实现高性能的网络数据包过滤、转发和路由。它可以用于改善网络性能、诊断网络问题并支持各种网络应用。
安全审计和监控
eBPF 可以用于监控系统的安全性跟踪恶意行为检测入侵并生成审计日志。它可以帮助系统管理员保持对系统安全性的可见性。
容器和云原生环境
eBPF 在容器和云原生环境中得到了广泛的应用。它可以用于监控容器的性能、安全性和行为以及在多个容器之间实现网络隔离和路由。
性能分析和跟踪
eBPF 可以用于性能分析和跟踪帮助开发人员识别和解决性能问题。它可以跟踪系统调用、函数执行时间、资源利用率等。
eBPF工具和库
为了更好地利用eBPF技术开发人员和系统管理员可以使用各种工具和库包括以下几种
bpftrace bpftrace 是一个高级工具用于跟踪和诊断系统性能问题。它提供了一种简洁的语法用于编写eBPF脚本以捕获和分析系统事件。
BCCBPF Compiler Collection BCC 是一个工具集提供了许多用于eBPF开发的工具和示例程序。它包括各种用于网络分析、性能分析和安全监控的工具。
libbpf libbpf 是一个库用于编写eBPF程序的用户空间工具。它提供了与内核交互的功能以及加载和执行eBPF程序的能力。
开始你的第一个 BPF 程序
BPF 程序的编译一般使用 LLVM.LLVM 是一种 genreal-purpose 的编译器LLVM 可以emit 不同的字节码。在本章中LLVM 将生成 bpf 的字节码然后我们会 load 到内核的虚拟中。 内核提供了一个系统调用专门用于 load bpf 的程序除了 load bpf 的程序这个系统调用还可以有一些其他的操作后面我们会看到它的用法接下来我们来看下 Hello world:
#include linux/bpf.h
#define SEC(NAME) __attribute__((section(NAME), used))
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...)
(void *)BPF_FUNC_trace_printk;
SEC(tracepoint/syscalls/sys_enter_execve)int bpf_prog(void *ctx) {char msg[] Hello, BPF World!;bpf_trace_printk(msg, sizeof(msg));return 0;
}
char _license[] SEC(license) GPL;编译命令
clang -O2 -target bpf -c bpf_program.c -I /usr/include/x86_64-linux-
gnu/ -o bpf_program.o我们使用 SEC 属性告诉 BPF VM,我们想在什么时候运行我们写的这个程序。在上面的代码中我们指定在 kernel 调用到 execve 的时候来调用我们自己写的程序。即SEC 中定义的是一个 Tracepoints,是 kernel 预先定义好的允许开发者在这里 injet 进去自己的代码。那你可能会问我怎么知道都有哪些 tracepoints 呢这个可以在/sys/kernel/debug/tracing/events/syscalls/这个目录下找到系统预留的所有的tracepoints. 另外我们需要使用指定 GPL 的协议。因为 kernel 本身就是 GPL 的。我们使用 bpf_trace_printk 来 打 印 在 内 核 中 生 成 的 日 志 。 当 然 你 也 可 以 通 过 /sys/kernel/debug/tracing/trace_pipe 来查看内核的日志。
我们需要一份源码来编译 libbpf
git clone --depth 1 git://kernel.ubuntu.com/ubuntu/ubuntu-bionic.git可以将源码拷贝到/kernel-src 目录下然后编译 libbpf
sudo mv ubuntu-bionic /kernel-srccd /kernel-src/tools/lib/bpfsudo make sudo make install prefix/usr/local现在我们有了 bpf 的代码需要一个程序把它 load 到内核中。
#include bpf_load.h
#include stdio.h
int main(int argc, char **argv) {if (load_bpf_file(bpf_program.o) ! 0) {printf(The kernel didnt load the BPF program\n);return -1;}read_trace_pipe();return 0;
}Makefile
CLANG clang
EXECABLE monitor-exec
BPFCODE bpf_program
BPFTOOLS /home/king/share/ubuntu-bionic/samples/bpf
BPFLOADER $(BPFTOOLS)/bpf_load.c
CCINCLUDE -I/home/king/share/ubuntu-
bionic/tools/testing/selftests/bpf
LOADINCLUDE -I/home/king/share/ubuntu-bionic/samples/bpf
LOADINCLUDE -I/home/king/share/ubuntu-bionic/tools/lib
LOADINCLUDE -I/home/king/share/ubuntu-bionic/tools/perf
LOADINCLUDE -I/home/king/share/ubuntu-bionic/tools/include
LIBRARY_PATH -L/usr/local/lib64
BPFSO -lbpf
.PHONY: clean $(CLANG) bpfload build
clean:rm -f *.o *.so $(EXECABLE)
build: ${BPFCODE.c} ${BPFLOADER}$(CLANG) -O2 -target bpf -c $(BPFCODE:.c) $(CCINCLUDE) -o
${BPFCODE:.o}
bpfload: buildclang -o $(EXECABLE) -lelf $(LOADINCLUDE) $(LIBRARY_PATH)
$(BPFSO) \$(BPFLOADER) loader.c
$(EXECABLE): bpfload
.DEFAULT_GOAL : $(EXECABLE)make 每执行一个 ls 就会有一行打印信息
bpftrace工具简介
bpftrace进行内核跟踪#### bpftrace 命令行操作单行命令工具
bpftrace -e programbpftrace直接跟-e选项后面加单行命令一些示例比如bpftrace -e BEGIN { printf(Hello world!\n); }
bpftrace -e kprobe:vfs_read { [tid] count();}
bpftrace -e kprobe:vfs_read /pid 123/ { [tid, comm] count();}
bpftrace -e t:block:block_rq_insert { [kstack] count(); }单行命令工具可以按ctrl-c也结束只有结束后才会打印输出结果。#### bpftrace 编写脚本工具
bpftrace支持脚本编写只需要在其实出添加#!/usr/local/bin/bpftrace会被认为是一个bpftrace脚本。#!/usr/local/bin/bpftrace
// this program times vfs_read()
kprobe:vfs_read
{start[tid] nsecs;
}
retprobe:vfs_read
/start[tid]/
{$duration_us (nsecs - start[tid]) / 1000;us hist($duration_us);delete(start[tid]);
}#### debug调试bpftrace -d
bpftrace -vbpftrace语法结构
bpftrace编程语言参考了awk的语法基础结构probes /filter/ { actions }它是一种事件驱动的运行方式。
probes表示的就是事件包括tracepoint、kprobe、kretprobe、uprobe等等。除了这些跟踪点还有两个特殊的事件BEGIN、END用于在脚本开始处和结束处执行。filter表示的是过滤条件当一个事件触发时会先判断该条件满足条件才会执行后面的action行为。action表示的具体执行的操作。
示例bpftrace -e kprobe:vfs_read /pid 123/ { [tid, comm] count();}#### bpftrace 脚本变量内部变量built-inuid: 用户id。
tid 线程id
pid 进程id。
cpu cpu id。
cgroupcgroup id.
probe 当前的trace点。
comm 进程名字。
nsecs 纳秒级别的时间戳。
kstack内核栈描述
curtask当前进程的task_struct地址。
args: 获取该kprobe或者tracepoint的参数列表
arg0: 获取该kprobe的第一个变量tracepoint不可用
arg1: 获取该kprobe的第二个变量tracepoint不可用
arg2: 获取该kprobe的第三个变量tracepoint不可用
retval: kretprobe中获取函数返回值
args-ret: kretprobe中获取函数返回值备注bpftrace -lv tracepoint:syscalls:sys_enter_read这个命令-lv可以用来查看一个tracepoint对应的参数都有哪些。自定义临时变量
以$标志起始来定义和引用一个变量$x 1Map变量
map变量是用于内核向用户空间传递数据的一种存储结构定义方式是以符号作为其实标记。
这个map可以有单个key或者多个keypath[tid] nsecs
path[pid, $fd] nsecsbpftrace默认在结束时会打印从内核接收到的map变量。#### 函数exit():退出bpftrace程序
str(char *):转换一个指针到string类型
system(format[, arguments ...]):运行一个shell命令
join(char *str[]):打印一个字符串列表并在每个前面加上空格比如可以用来输出args-argv
ksym(addr)用于转换一个地址到内核symbol
kaddr(char *name):通过symbol转换为内核地址
print(m [, top [, div]])可选择参数打印map中的top n个数据数据可选择除以一个div值#### map函数
bpftrace内构的一些map函数用于传递数据给map变量注意接收他们的变量是map类型。常用的包括count():用于计算次数
sum(int n):用于累加计算
avg(int n):用于计算平均值
min(int n):用于计算最小值
max(int n):用于计算最大值
hist(int n):数据分布直方图范围为2的幂次增长
lhist(int n)数据线性直方图
delete(m[key]):删除map中的对应的key数据
clear(m):删除map中的所有数据
zero(m):map中的所有值设置为0#### 附件
常用的一些单行命令示例bpftrace -e tracepoint:block:block_rq_i* { [probe] count(); } interval:s:1 { print(); clear(); }
bpftrace -e tracepoint:syscalls:sys_exit_read /args-ret 0/ { bytes sum(args-ret); }
bpftrace -e tracepoint:syscalls:sys_exit_read { ret hist(args-ret); }
bpftrace -e tracepoint:syscalls:sys_exit_read { ret lhist(args-ret, 0, 1000, 100); }
bpftrace -e tracepoint:syscalls:sys_exit_read /args-ret 0/ { [- args-ret] count(); }
bpftrace -e kprobe:vfs_* { [probe] count(); } END { print(, 5); clear(); }
bpftrace -e kprobe:vfs_read { start[tid] nsecs; } kretprobe:vfs_read /start[tid]/ { ms[comm] sum(nsecs - start[tid]); delete(start[tid]); } END { print(ms, 0, 1000000); clear(ms); clear(start); }
bpftrace -e k:vfs_read { [pid] count(); }
bpftrace -e tracepoint:syscalls:sys_enter_execve { printf(%s - %s\n, comm, str(args-filename)); }
bpftrace -e tracepoint:syscalls:sys_enter_execve { join(args-argv); }
bpftrace -e tracepoint:syscalls:sys_enter_openat { printf(%s %s\n, comm, str(args-filename)); }
bpftrace -e tracepoint:raw_syscalls:sys_enter {[comm] count(); }
bpftrace -e tracepoint:syscalls:sys_enter_* {[probe] count(); }
bpftrace -e tracepoint:raw_syscalls:sys_enter {[pid, comm] count(); }
bpftrace -e tracepoint:syscalls:sys_exit_read /args-ret/ { [comm] sum(args-ret); }
bpftrace -e tracepoint:syscalls:sys_exit_read { [comm] hist(args-ret); }
bpftrace -e tracepoint:block:block_rq_issue { printf(%d %s %d\n, pid, comm, args-bytes); }
bpftrace -e software:major-faults:1 { [comm] count(); }
bpftrace -e software:faults:1 { [comm] count(); }
bpftrace -e tracepoint:syscalls:sys_enter_clone { printf(- clone() by %s PID %d\n, comm, pid); } tracepoint:syscalls:sys_exit_clone { printf(- clone() return %d, %s PID %d\n, args-ret, comm, pid); }
bpftrace -e tracepoint:syscalls:sys_enter_setuid { printf(setuid by PID %d (%s), UID %d\n, pid, comm, uid); }
bpftrace -e tracepoint:syscalls:sys_exit_setuid { printf(setuid by %s returned %d\n, comm, args-ret); }
bpftrace -e tracepoint:block:block_rq_insert { printf(Block I/O by %s\n, kstack); }
bpftrace -e tracepoint:syscalls:sys_enter_connect /pid 123/ { printf(PID %d called connect()\n, $1); }
bpftrace -e tracepoint:timer:hrtimer_start { [ksym(args-function)] count(); }
bpftrace -e t:syscalls:sys_enter_read { reads count(); } interval:s:5 { exit(); }
bpftrace -e kprobe:vfs_read {ID pid;} interval:s:2 {printf(ID:%d\n, ID);}
这只是简介留下个以后详细学习的地址 地址1
地址2
结论
eBPF 技术已经成为 Linux 内核中的一个强大工具用于解决性能、安全性和可观察性等各种挑战。通过可编程性和灵活性eBPF 为开发人员和系统管理员提供了解决问题的新途径为未来的系统优化和安全性提供了更多的机会。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/932480.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!