Linux动态追踪——eBPF

news2024/11/18 14:01:17

目录

摘要 

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_t
    event = bpf["events"].event(data)
    if start == 0:
            start = event.ts
    time_s = (float(event.ts - start)) / 1000000000
    print("%-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.coloradmin.cn/o/1621129.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Json-server 模拟后端接口

json-server&#xff0c;模拟rest接口&#xff0c;自动生成增删改查接口。(官网地址&#xff1a;json-server - npm) 使用方法&#xff1a; 1. 安装json-server&#xff0c;npm i json-server -g 2. 创建json文件&#xff0c;文件中存储list数据&#xff0c;db.json {"…

路由器本地docker 下载node容器部署 thressjs文档

1. 每次启动本地文档太麻烦 &#xff0c;路由器刚好支持docker&#xff08;tp-link6088&#xff09; &#xff0c;部署上去自启动 2.

简述MASM宏汇编

Hello , 我是小恒不会java。今天写写x86相关底层的东西 寄存器 8086由BIU和EU组成 8088/8086寄存器有14个。8通用&#xff0c;4段&#xff0c;1指针&#xff0c;1标志 8个通用寄存器&#xff1a;这些寄存器可以用来存储任意类型的数据&#xff0c;包括整数、地址等。8086有8个…

PyTorch Conv2d 前向传递中发生了什么?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

如何更好的管理个人财务?使用极空间部署私有记账系统Firefly III

如何更好的管理个人财务&#xff1f;使用极空间部署私有记账系统Firefly III 哈喽小伙伴们好&#xff0c;我是Stark-C~ 不知道屏幕前的各位“富哥”日常生活中是怎么管理自己巨额财富的&#xff0c;反正对于像我这样年薪过千的摸鱼族来说&#xff0c;请一个专业的理财顾问多多…

【论文阅读】互连网络的负载平衡路由算法 (RLB RLBth)

前言Oblivious Load Balancing 不经意路由负载平衡 1. oblivious routing 不经意/无关路由的背景知识 1. oblivious routing, adaptive routing & minimal/non-minimal routing algorithms 2. Balancing a 1-Dimensional ring: RLB and RLBth 一维 ring 的 RLB and RLBth 1…

JavaEE初阶之IO流快速顿悟一(超详细)

目录 题外话 正题 IO流 Java.io.FileInputStream int read() int read(byte[] b) 关于异常 Java7的新特性: try-with-resources ( 资源自动关闭) Java.io.FileOutputStream void write(int b) void write(byte[] b) 小结 题外话 十年青铜无人问,一朝顿悟冲王者 前天…

会声会影滤镜怎么用 会声会影滤镜效果怎么调 会声会影视频制作教程

在进行视频剪辑时&#xff0c;合理地运用滤镜效果可以提升视频的观赏性&#xff0c;使你的作品更加出彩。这篇文章便一起来学习会声会影滤镜怎么用&#xff0c;会声会影滤镜效果怎么调。 一、会声会影滤镜怎么用 使用会声会影的滤镜效果非常简单&#xff0c;以下是具体的操作…

Linux部署MySQL

部署MySQL 先停掉虚拟机中的MySQL&#xff0c;确保你的虚拟机已经安装Docker&#xff0c;且网络开通的情况下&#xff0c;执行下面命令即可安装MySQL&#xff1a; docker run -d \ --name mysql \ -p 3306:3306 \ -e TZAsia/Shanghai \ -e MYSQL_ROOT_PASSWORD123 \ mysql 安…

13. Spring AOP(一)思想及使用

1. 什么是Spring AOP AOP的全称是Aspect Oriented Programming&#xff0c;也就是面向切面编程&#xff0c;是一种思想。它是针对OOP(面向对象编程)的一种补充&#xff0c;是对某一类事情的集中处理。比如一个博客网站的登陆验证功能&#xff0c;在用户进行新增、编辑、删除博…

【算法分析与设计】重复的DNA

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 题目 DNA序列 由一系列核苷酸组成&#xff0c;缩写为 A, C, G 和 T.。 例如&#xff0c;"ACGAATTCCG" 是一个 DNA序列 。 在研究…

【架构方法论(一)】架构的定义与架构要解决的问题

文章目录 一. 架构定义与架构的作用1. 系统与子系统2. 模块与组件3. 框架与架构4. 重新定义架构&#xff1a;4R 架构 二、架构设计的真正目的-别掉入架构设计的误区1. 是为了解决软件复杂度2. 简单的复杂度分析案例 三. 案例思考 本文关键字 架构定义 架构与系统的关系从业务逻…

前端零代码开发实践:页面嵌套+逻辑连线0开发扩展组件,实现切换开关控制扇叶转动。能无代码封装扩展组件,有别于常规的web组态或低代码平台

前言&#xff1a; 官网:http://www.uiotos.net/ 什么是 UIOTOS&#xff1f; 这是一款拥有独创专利技术的前端零代码工具&#xff0c;专注于解决前端界面开发定制难题&#xff0c;原型即应用&#xff01;具有页面嵌套、属性继承、节点连线等全新特性&#xff0c;学习门槛低…

OpenCV 如何实现边缘检测器

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何实现拉普拉斯算子的离散模拟 下一篇 :OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数…

基于RK3588的全国产鸿蒙边缘计算工控机在智能交通ETC收费系统的应用

1.1 产品简介 基于智能交通、工业互联等行业快速智能化发展的需求&#xff0c;以 OpenHarmony 为框架开发嵌入 HamonyOS&#xff0c;打造了具有高智能、高可靠、高安全的自主 可控的边缘处理器 XM-RK3588。 图 1-1 边缘处理器 HamonyOS强化 IoT 互联互动能力&#xff0c;让边缘…

【JAVA基础之IO】字节流、字符流以及乱码问题

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1. IO概述 1.1 什么是IO 1.2 IO的分类 1.3 字节和字符流的顶级父类 2. 字节流 2.1 一切皆为字节 2.2 字节输出流【OutputStream】 2.3 FileOutputStream类…

【Python】自定义修改pip下载模块默认的安装路径

因为电脑下载了Anaconda提供的默认Python 3.9 以及后期下载的python3.10所以在Pychram进行项目开发时&#xff0c;发现一些库怎么导入都导入不了&#xff0c;手动install也是失败&#xff0c;后期在cmd里面发现python以及pip配置有点儿混乱&#xff0c;导致执行命令时&#xff…

学习c语音的自我感受

因为是自学&#xff0c;所以走过不少弯路。去年&#xff0c;受知乎“python性能弱”风潮的影响&#xff0c;学过go,rust。 在学习这些新语言的时候&#xff0c;由衷感受到&#xff0c;或是本身侧重方向的原因&#xff08;如go侧重服务器&#xff09;&#xff0c;或是语言太新不…

01-服务与服务间的通信

这里是极简版&#xff0c;仅用作记录 概述 前端和后端可以使用axios等进行http请求 服务和服务之间也是可以进行http请求的spring封装的RestTemplate可以进行请求 用法 使用bean注解进行依赖注入 在需要的地方&#xff0c;自动注入RestTemplate进行服务和服务之间的通信 注…

探索React Router:实现动态二级路由

我有一个路由配置的二维数组&#xff0c;想根据这个数组结合路由组件来动态生成路由&#xff0c;应该怎么样实现。在 React Router 6 中渲染二级路由的方式跟 React Router 65相比有一些变化,但核心思路仍然是利用 Route 组件和路由嵌套的方式。下面是具体的步骤: 定义路由数组…