Linux动态追踪——eBPF

ops/2024/9/20 1:27:00/ 标签: linux, 服务器

目录

摘要 

1 什么是 eBPF

2 eBPF 支持的功能

3 BCC

4 编写脚本

5 总结

6 附


摘要 

        ftrace 和 perf 与 ebpf 同为 linux 内核提供的动态追踪工具,其中 ftrace 侧重于事件跟踪和内核行为的实时分析,perf 更侧重于性能分析和事件统计,与二者相比,ebpf 则更为强大,拥有更高的灵活性。

1 什么是 eBPF

        eBPF 可以理解为是 kernel 中实现的一个虚拟机机制,其拥有自定义的 64 位 RISC 指令集,可以再 linux 内核中运行即时编译的 BPF 程序,支持访问内核功能和内存的部分子集。具体原理为它会将类 C 代码编译为 BPF 字节码,类似 Java 字节码。然后将对应的程序代码挂到内核的钩子上,当钩子函数被调用时,内核将在 eBPF 虚拟机这个沙盒中执行字节码。

        在内核内运行一个完整的虚拟机主要是考虑便利和安全。虽然 eBPF 程序所做的操作都可以通过正常的内核模块来处理,但直接的内核编程是一件非常危险的事情 - 这可能会导致系统锁定、内存损坏和进程崩溃,从而导致安全漏洞和其他意外的效果,特别是在生产设备上(eBPF 经常被用来检查生产中的系统),所以通过一个安全的虚拟机运行本地 JIT 编译的快速内核代码对于安全监控和沙盒、网络过滤、程序跟踪、性能分析和调试都是非常有价值的。部分简单的样例可以在这篇优秀的 Linux Extended BPF (eBPF) Tracing Tools 中找到。

基于设计,eBPF 虚拟机和其程序有意地设计为不是图灵完备的:即不允许有循环(正在进行的工作是支持有界循环【译者注:已经支持有界循环,#pragma unroll 指令】),所以每个 eBPF 程序都需要保证完成而不会被挂起、所有的内存访问都是有界和类型检查的(包括寄存器,一个 MOV 指令可以改变一个寄存器的类型)、不能包含空解引用、一个程序必须最多拥有 BPF_MAXINSNS 指令(默认 4096)、“主"函数需要一个参数(context)等等。当 eBPF 程序被加载到内核中,其指令被验证模块解析为有向环状图,上述的限制使得正确性可以得到简单而快速的验证。

2 eBPF 支持的功能

        eBPF 常用来编写网络程序,将该网络程序附加到网络socket,进行流量过滤,流量分类以及执行网络分类器的动作。eBPF程序甚至可以修改一个已建链的网络socket的配置。例如 XDP 会在网络栈的底层运行 eBPF 程序,高性能地进行处理接收到的报文。另外就是用来调试了。

        下面这张图展示了 eBPF 的工作原理,大致可以分为 4 步:

  1. 编写 eBPF 代码,并生成字节码
  2. 将 eBPF 代码加载到内核的 eBPF 框架(注册 eBPF 程序到 kernel 钩子上)
  3. 事件发生,开始执行 eBPF 程序,记录数据到 maps 中
  4. 读取 maps 数据

        由于 eBPF 偏底层,平常如果是调式使用直接用底层 kernel 的接口写是比较麻烦的,幸运的是 BCC 工具集提供了好用的封装。

3 BCC

        在 eBPF 执行过程中,对于编译、加载还有 maps 等操作,对所有的跟踪程序来说都是通用的。把这些过程通过 Python 抽象起来,也就诞生了 BCC(BPF Compiler Collection)。
        BCC 把 eBPF 中的各种事件源(比如 kprobe、uprobe、tracepoint 等)和数据操作(称为 Maps),也都转换成了 Python 接口(也支持 lua)。这样,使用 BCC 进行动态追踪时,编写简单的脚本就可以了。因为需要跟内核中的数据结构交互,所以真正核心的事件处理逻辑,还是需要用 C 语言来编写。

        安装 BCC 后,在 tools 目录下已经提供了一系列的工具,并在 tools/doc 目录下提供了各工具对应的使用说明了:

[root@centos ~]# yum install bcc-tools[root@centos ~]# ls /usr/share/bcc/tools/
argdist       capable       drsnoop         hardirqs     memleak         perlcalls    pythonstat   slabratetop  tclobjnew   tplist
bashreadline  cobjnew       execsnoop       javacalls    mountsnoop      perlflow     reset-trace  sofdsnoop    tclstat     trace
...[root@centos ~]# ls /usr/share/bcc/tools/doc/
argdist_example.txt       execsnoop_example.txt       mountsnoop_example.txt      reset-trace_example.txt  tclstat_example.txt
bashreadline_example.txt  ext4dist_example.txt        mysqld_qslower_example.txt  rubycalls_example.txt    tcpaccept_example.txt
biolatency_example.txt    ext4slower_example.txt      nfsdist_example.txt         rubyflow_example.txt     tcpconnect_example.txt
biosnoop_example.txt      filelife_example.txt        nfsslower_example.txt       rubygc_example.txt       tcpconnlat_example.txt
...

        例如直接用 BCC 提供的 trace 工具跟踪 do_sys_open 函数调用:

[root@centos ~]# /usr/share/bcc/tools/trace  'do_sys_open "%s", arg2' -T
TIME     PID     TID     COMM            FUNC             -
14:30:06 2899    2954    YDService       do_sys_open      /proc/sys/kernel/random/uuid
14:30:06 32584   32584   sh              do_sys_open      ../lib/tls/x86_64/libtinfo.so.5
14:30:06 32584   32584   sh              do_sys_open      ../lib/tls/libtinfo.so.5
14:30:06 32584   32584   sh              do_sys_open      ../lib/x86_64/libtinfo.so.5
14:30:06 32584   32584   sh              do_sys_open      ../lib/libtinfo.so.5

4 编写脚本

        有时候提供的工具不能很好的满足我们的需求,可能需要手动开发一个脚本来方便调试。假设还是跟踪 do_sys_open,它长这样:

// include/linux/fs.h
extern long do_sys_open(int dfd, const char __user *filename, int flags,umode_t mode);

        编写这样一个脚本同之前讲的 eBPF 原理一一对应,大体分为四步:

1. 定义事件处理函数

#!/bin/python
from bcc import BPF# 1.define BPF program (""" is used for multi-line string).
prog = """
#include <uapi/linux/ptrace.h>
#include <uapi/linux/limits.h>
#include <linux/sched.h>
// define output data structure in C
struct data_t {u32 pid;u64 ts;char comm[TASK_COMM_LEN];int dfd;char fname[NAME_MAX];int flags;unsigned short mode;
};
BPF_PERF_OUTPUT(events);// define the handler for do_sys_open.
// ctx is required, while other params depends on traced function.
int user_cb(struct pt_regs *ctx, int dfd, const char __user *filename, int flags, umode_t mode){struct data_t data = {};data.pid = bpf_get_current_pid_tgid();data.ts = bpf_ktime_get_ns();if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) {data.dfd = dfd;bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);data.flags = flags;data.mode = mode;}events.perf_submit(ctx, &data, sizeof(data));return 0;
}
"""

        这个函数以受限 C 语言来编写,作用是定义事件处理的逻辑。这部分代码需要以字符串的形式送到 eBPF 模块中处理。

2. 将 eBPF 加载到内核中

# 2.load BPF program
bpf = BPF(text=prog)
# attach the kprobe for do_sys_open, and set handler to user_cb
bpf.attach_kprobe(event="do_sys_open", fn_name="user_cb")

3. 读取 maps 数据

# 3.process event
start = 0
def print_event(cpu, data, size):global start# event's type is data_tevent = bpf["events"].event(data)if start == 0:start = event.tstime_s = (float(event.ts - start)) / 1000000000print("%-18.9f %-16s %-6d %-8s %-8s %-8s %-16s" % (time_s, event.comm, event.pid, hex(event.dfd), hex(event.flags), hex(event.mode), event.fname))# loop with callback to print_event
bpf["events"].open_perf_buffer(print_event)
print("%-18s %-16s %-6s %-8s %-8s %-8s %-16s" % ("TIME(s)", "COMM", "PID", "DFD", "FLAGS", "MODE", "FILE"))

4. 等待内核事件的发生

# 4 start the event polling loop
while 1:try:bpf.perf_buffer_poll()except KeyboardInterrupt:exit()

        将上面的几个步骤完整的串起来执行,不出意外的话就可以得到执行结果了:

[root@centos ~]# ./ebpf_test.py 
TIME(s)            COMM             PID    DFD      FLAGS    MODE     FILE            
0.000000000        barad_agent      6364   -0x64    0x8000   0x1b6    /proc/meminfo   
0.000116077        barad_agent      6364   -0x64    0x8000   0x1b6    /proc/meminfo   
0.000261420        barad_agent      6364   -0x64    0x8000   0x1b6    /proc/vmstat    
0.000464681        ebpf_test.py     6363   -0x64    0x8000   0x1b6    /usr/lib64/python2.7/encodings/ascii.so
0.000476553        ebpf_test.py     6363   -0x64    0x8000   0x1b6    /usr/lib64/python2.7/encodings/asciimodule.so
0.000482585        ebpf_test.py     6363   -0x64    0x8000   0x1b6    /usr/lib64/python2.7/encodings/ascii.py
0.000492303        ebpf_test.py     6363   -0x64    0x8000   0x1b6    /usr/lib64/python2.7/encodings/ascii.pyc

        从输出中可以看到 ebpb_test.py 自身为了打印也是打开了一堆的 so 库~~通

5 总结

        通过对 eBPF 相关资料的查询,了解了它的概念及基本原理,下一阶段的学习路径就是要熟练使用 BCC 提供的工具集,以及必要时刻能够改造 BCC 提供的工具或者自己手写一个合适的工具以方便排查问题。

6 附

        贴上完整的源码:Linux/ebpf_test.py at master · Fireplusplus/Linux · GitHub


http://www.ppmy.cn/ops/7800.html

相关文章

达梦数据库的监听进程

达梦数据库&#xff08;DMDB&#xff09;也同样有监听进程&#xff0c;其主要职责是监听来自客户端的连接请求。这里的监听进程指的是达梦数据库服务启动后&#xff0c;在操作系统级别监听特定TCP/IP端口&#xff08;默认情况下是5236&#xff0c;但这可以配置&#xff09;的进…

Spring Security之Session管理

前言 在聊认证过滤器的时候&#xff0c;我们埋了个坑&#xff1a;Session管理。实际上&#xff0c;事情从这里开始&#xff0c;就变得复杂了。提前跟大家交个底&#xff1a;后续我们将涉及多个需要协同才能完成的功能。 什么是Session 想要管理session&#xff0c;就必须搞清…

Web前端框架/库/工具

前言 前端从步枪&#xff08;原生js&#xff09;到了半自动武器&#xff08;jQuery&#xff09;并进化为全自动武器&#xff08;三大框架&#xff08;angular&#xff0c;react&#xff0c;vue及其生态链&#xff09;&#xff09;。 常说工欲善其事必先利其器。对于那些想要提…

TPCC MySQL

目录 1. tpcc-mysql下载 2. tpcc-mysql安装 3. 初始化tpcc-mysql 4. 使用tpcc-mysql测试 5. 结果解释 1. tpcc-mysql下载 https://github.com/Percona-Lab/tpcc-mysql 2. tpcc-mysql安装 [rootlocalhost soft]# unzip tpcc-mysql-master.zip [rootlocalhost soft]# cd t…

代码随想录训练营24day-贪心算法2

一、122 买卖股票最佳时机 题目介绍限制条件&#xff0c;必须卖了再买&#xff0c;而且当前交易一只股票。一开始想法是去遍历&#xff0c;找到每个区间段间的差值&#xff0c;然后再相加。看了解答&#xff0c;其实每一天的利润&#xff0c;都是可以用差值表示出来&#xff0…

Spring - 1 ( 8000 字 Spring 入门级教程 )

一&#xff1a;SpringBoot 快速上手 环境准备 ⾃检Idea版本: 社区版: 2021.1 -2022.1.4专业版: ⽆要求 如果个⼈电脑安装的idea不在这个范围, 需要卸载重新安装.&#xff08;⼀定要删除注册表&#xff09; Maven Maven是⼀个项⽬管理⼯具。基于POM(Project Object Model,…

centos 7.9 安装 ftp 传输文件

ftp server 端 sudo yum install vsftpd ftp其中 vsftpd 为 ftp server 端&#xff0c;ftp 包含 ftp 这个客户端命令。 # sudo rpm -ql vsftpd/etc/logrotate.d/vsftpd /etc/pam.d/vsftpd /etc/vsftpd /etc/vsftpd/ftpusers /etc/vsftpd/user_list /etc/vsftpd/vsftpd.conf /…

耀斑层-如何在Unity中实现耀斑亮光效果

在Unity中实现耀斑亮光效果可以通过以下步骤来实现&#xff1a; 创建一个空物体作为光源&#xff1a;在场景中创建一个空物体&#xff0c;并将其放置在需要发出耀斑亮光效果的位置上。 添加光源组件&#xff1a;选中空物体&#xff0c;在Inspector面板中点击"Add Compone…

C# 下记录(Record)详解

在C# 9.0中&#xff0c;引入了一个新的关键字&#xff1a;record。record关键字用于定义记录类型&#xff0c;这是一种不可变的数据结构&#xff0c;用于表示具有明确字段名称和类型的数据集。本文将详细介绍C#中record类型的使用和特点&#xff0c;以及如何通过记录记录器&…

使用easyexcel将csv转为excel

一.背景 供应商系统下载的csv文件不支持域控&#xff08;主要是第三方wps服务不能对csv文件加密&#xff0c;但是可以对office系列产品进行权限访问的加密控制&#xff09;。因此思路就改为现将csv文件转为excel文件&#xff0c;然后对excel文件进行加域控制。本文主要介绍如何…

数据结构--双向链表

在讲双向链表之前&#xff0c;我们先了解一下链表的分类&#xff1a; 链表的结构⾮常多样&#xff0c;主要分为带头与不带头、单向与双向、循环与不循环。三个种类可以任意搭配&#xff0c;所以总共可以形成八种链表&#xff0c;但是最常用的是单向不带头不循环链表和双向带头循…

html、css、QQ音乐移动端静态页面,资源免费分享,可作为参考,提供InsCode在线运行演示

CSDN将我上传的免费资源私自变成VIP专享资源&#xff0c;且作为作者的我不可修改为免费资源&#xff0c;不可删除&#xff0c;寻找客服无果&#xff0c;很愤怒&#xff0c;&#xff08;我发布免费资源就是希望大家能免费一起用、一起学习&#xff09;&#xff0c;接下来继续寻找…

代码托管基础操作

在待上传代码文件夹中右键&#xff0c;打开Git Bash Here依次输入以下命令&#xff1a; git init(在本地初始化一个代码仓库&#xff0c;具体表现为会在你的文件夹里出现一个隐藏的.git文件夹) git add .&#xff08;先把代码放到本地的一个缓冲区&#xff09;添加当前目录下的…

命理八字之答案之书前端uniapp效果实现

#uniapp# #答案之书# 不讲废话&#xff0c;先上截图 <div class"padding"><div class"flex align-center justify-center" style"padding-top:100px;"><div class"radarContainer"><div id"radarBox"…

初识ansible变量及实例配置

目录 1、为什么要使用变量 2、变量分类 3、 变量详解 3.1 vars,vars_files , group_vars 3.1 .1 vars 剧本中定义变量 3.1.2 vars_file 将变量存放到一个文件中&#xff0c;并在剧本中引用 3.1.3 group_vars 创建一个变量文件给某个组使用 实例1-根据不同的主机…

[ LeetCode ] 题刷刷(Python)-第35题:搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 nums 为 无重复元素 的 升序 排列数组 请必须使用时间复杂度为 O(log n) 的算法。 示例 示例 1: 输入: …

Django老项目升级到新版本

手上有个 Django 老项目&#xff0c;一直跑得好好的&#xff0c;好几年没动过了&#xff0c;维护费收得正爽&#xff0c;没想到客户来了个新的运营人员&#xff0c;丢了个改动需求过来。我一看也没啥大改&#xff0c;就答应了。大意了。 问题 刚开始改&#xff0c;我这种老鸟…

MongoDB聚合运算符:$sampleRate

MongoDB聚合运算符&#xff1a;$sampleRate 文章目录 MongoDB聚合运算符&#xff1a;$sampleRate语法使用举例 $sampleRate聚合运算符用$match&#xff0c;按照指定的抽样比例&#xff0c;从输入的文档中随机选择相应的文档。 语法 { $sampleRate: <non-negative float>…

使用Spring Boot整合定时任务(Schedule)

1、添加依赖&#xff1a; 在pom.xml文件中添加Spring Boot的定时任务依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId> </dependency> 2、创建定时任务类&#xff1a; 创建…

搜索+剪枝,LeetCode 216. 组合总和 III

目录 一、题目 1、题目描述 2、接口描述 python3 cpp 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 python3 cpp 一、题目 1、题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多…