使用eBPF追踪Linux内核

news2025/1/13 13:39:26

简介

BPF,及伯克利包过滤器Berkeley Packet Filter,最初构想提出于 1992 年,其目的是为了提供一种过滤包的方法,并且要避免从内核空间到用户空间的无用的数据包复制行为。它最初是由从用户空间注入到内核的一个简单的字节码构成,它在那个位置利用一个校验器进行检查 —— 以避免内核崩溃或者安全问题 —— 并附着到一个套接字上,接着在每个接收到的包上运行。几年后它被移植到 Linux 上,并且应用于一小部分应用程序上(例如,tcpdump)。其简化的语言以及存在于内核中的即时编译器(JIT),使 BPF 成为一个性能卓越的工具。

然后,在 2013 年,Alexei Starovoitov 对 BPF 进行彻底地改造,并增加了新的功能,改善了它的性能。这个新版本被命名为 eBPF (意思是 “extended BPF”),与此同时,将以前的 BPF 变成 cBPF(意思是 “classic” BPF)。新版本出现了如映射和尾调用tail call这样的新特性,并且 JIT 编译器也被重写了。新的语言比 cBPF 更接近于原生机器语言。并且,在内核中创建了新的附着点。

感谢那些新的钩子,eBPF 程序才可以被设计用于各种各样的情形下,其分为两个应用领域。其中一个应用领域是内核跟踪和事件监控。BPF 程序可以被附着到探针(kprobe),而且它与其它跟踪模式相比,有很多的优点(有时也有一些缺点)。

另外一个应用领域是网络编程。除了套接字过滤器外,eBPF 程序还可以附加到 tc(Linux 流量控制工具)的入站或者出站接口上,以一种很高效的方式去执行各种包处理任务。这种使用方式在这个领域开创了一个新的天地。

并且 eBPF 通过使用为 IO Visor 项目开发的技术,使它的性能进一步得到提升:也为 XDP(“eXpress Data Path”)添加了新的钩子,XDP 是不久前添加到内核中的一种新式快速路径。XDP 与 Linux 栈组合,然后使用 BPF ,使包处理的速度更快。

甚至一些项目,如 P4、Open vSwitch,考虑 或者开始去接洽使用 BPF。其它的一些,如 CETH、Cilium,则是完全基于它的。BPF 是如此流行,因此,我们可以预计,不久之后,将围绕它有更多工具和项目出现 …

什么是BPF程序:

BPF is a highly flexible and efficient virtual machine-like construct in the Linux kernel allowing to execute bytecode at various hook points in a safe manner.
  1. BPF程序 ----LLVM+Clang----> BPF字节码 ----JIT----> BPF指令集;
  2. BPF架构采用一种新的虚拟机设计,包含支持x86_64, arm64, mips64等架构的指令集,BPF指令集程序可以高效地工作在基于寄存器架构(r0到r10)的CPU上;
  3. Linux内核维护者不断开发hook点,可以在hook点上挂载BPF程序,当hook点对应的事件发生就可以执行BPF程序,BPF程序返回hook点预定义的值,Linux内核再根据返回值执行下一步操作(比如XDP类型的BPF程序挂载在指定的网络接口上,有数据包进入该网络接口,BPF程序对数据包进行解析然后根据协议字段进行判断,如果不符合规则就返回XDP_DROP,Linux内核根据该返回值就会丢弃该数据包)。

BPF工作原理:

内核资料直通车:最新Linux内核源码资料文档+视频资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

BPF关键组件:

  • BPF Hooks
  • BPF映射:
  1. BPF程序和用户空间程序通过BPF映射通信;
  2. BPF映射以键/值保存在内核,可以被任何BPF程序访问,用户空间的程序可以通过文件描述符访问BPF映射
  3. BPF映射类型:BPF映射支持多种数据结构,从而实现内核内部数据的组织以及用户态和内核态的通信,比如哈希表、数组、队列等等

BPF映射用途举例:

1)在BPF程序不中断的情况下修改其运行方式,修改映射中BPF程序访问的配置数据或应用数据,例如黑名单规定的IP列表和域名;

2)运行在内核的BPF程序统计进入指定网络接口的数据包信息,并将统计信息保存到BPF映射,用户态程序可以通过BPF映射获取数据包统计信息;

  • BPF辅助函数(BPF Helper Function):如其他语言生态会提供丰富的库提供大量的API函数,BPF也包含各种常用的辅助函数,提供操作内核数据和BPF映射的工具类函数;

 资料直通车:最新Linux内核源码资料文档+视频资料

内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

  1. 优点:通过定义和维护BPF辅助函数,由BPF辅助函数维护者处理Linux内核版本的迭代更新,对开发者透明,形成稳定的API接口;
  2. BPF辅助函数列表:

我们可以使用BPF对Linux内核进行跟踪,收集我们想要的内核数据,从而对Linux中的程序进行分析和调试。与其它的跟踪技术相比,使用BPF的主要优点是几乎可以访问Linux内核和应用程序的任何信息,同时,BPF对系统性能影响很小,执行效率很高,而且开发人员不需要因为收集数据而修改程序。

本文将介绍保证BPF程序安全的BPF验证器,然后以BPF程序的工具集BCC为例,分享kprobes和tracepoints类型的BPF程序的使用及程序编写示例。

BPF验证器

BPF借助跟踪探针收集信息并进行调试和分析,与其它依赖于重新编译内核的工具相比,BPF程序的安全性更高。重新编译内核引入外部模块的方式,可能会因为程序的错误而产生系统奔溃。BPF程序的验证器会在BPF程序加载到内核之前分析程序,消除这种风险。

BPF验证器执行的第一项检查是对BPF虚拟机加载的代码进行静态分析,目的是确保程序能够按照预期结束。验证器在进行第一项检查时所做工作为:

  • 程序不包含控制循环;
  • 程序不会执行超过内核允许的最大指令数;
  • 程序不包含任何无法到达的指令;
  • 程序不会超出程序界限。

BPF验证器执行的第二项检查是对BPF程序进行预运行,所做工作为:

  • 分析BPF程序执行的每条指令,确保不会执行无效指令;
  • 检查所有内存指针是否可以正确访问和引用;
  • 预运行将程序控制流的执行结果通知验证器,确保BPF程序最终都会执行BPF_EXIT指令。

内核探针 kprobes

内核探针可以跟踪大多数内核函数,并且系统损耗最小。当跟踪的内核函数被调用时,附加到探针的BPF代码将被执行,之后内核将恢复正常模式。

kprobes类BPF程序的优缺点

  • 优点 动态跟踪内核,可跟踪的内核函数众多,能够提取内核绝大部分信息。
  • 缺点 没有稳定的应用程序二进制接口,可能随着内核版本的演进而更改。

kprobes

kprobe程序允许在执行内核函数之前插入BPF程序。当内核执行到kprobe挂载的内核函数时,先运行BPF程序,BPF程序运行结束后,返回继续开始执行内核函数。下面是一个使用kprobe的bcc程序示例,功能是监控内核函数kfree_skb函数,当此函数触发时,记录触发它的进程pid,进程名字和触发次数,并打印出触发此函数的进程pid,进程名字和触发次数:

#!/usr/bin/python3
# coding=utf-8
from __future__ import print_function
from bcc import BPF
from time import sleep
# define BPF program

bpf_program = """
#include <uapi/linux/ptrace.h>
struct key_t{
	u64 pid;
};
BPF_HASH(counts, struct key_t);
int trace_kfree_skb(struct pt_regs *ctx) {
	u64 zero = 0, *val, pid;
	pid = bpf_get_current_pid_tgid() >> 32;
	struct key_t key  = {};
	key.pid = pid;
    val = counts.lookup_or_try_init(&key, &zero);
    if (val) {
      (*val)++;
    }
    return 0;
}
"""

def pid_to_comm(pid):
    try:
        comm = open("/proc/%s/comm" % pid, "r").read().rstrip()
        return comm
    except IOError:
        return str(pid)

# load BPF

b = BPF(text=bpf_program)
b.attach_kprobe(event="kfree_skb", fn_name="trace_kfree_skb")

# header
print("Tracing kfree_skb... Ctrl-C to end.")
print("%-10s %-12s %-10s" % ("PID", "COMM", "DROP_COUNTS"))

while 1:
	sleep(1)
	for k, v in sorted(b["counts"].items(),key = lambda counts: counts[1].value):
	  	print("%-10d %-12s %-10d" % (k.pid, pid_to_comm(k.pid), v.value))

该bcc程序主要包括两个部分,一部分是python语言,一部分是c语言。python部分主要做的工作是BPF程序的加载和操作BPF程序的map,并进行数据处理。c部分会被llvm编译器编译为BPF字节码,经过BPF验证器验证安全后,加载到内核中执行。python和c中出现的陌生函数可以查下面这两个手册,在此不再赘述:

python部分遇到的陌生函数可以查这个手册: 点此跳转

c部分中遇到的陌生函数可以查这个手册: 点此跳转

需要说明的是,该BPF程序类型是kprobe,它是在这里进行程序类型定义的:

b.attach_kprobe(event="kfree_skb", fn_name="trace_kfree_skb")
  • b.attach_kprobe()指定了该BPF程序类型为kprobe;
  • event="kfree_skb"指定了kprobe挂载的内核函数为kfree_skb;
  • fn_name="trace_kfree_skb"指定了当检测到内核函数kfree_skb时,执行程序中的trace_kfree_skb函数;
BPF程序的第一个参数总为ctx,该参数称为上下文,提供了访问内核正在处理的信息,依赖于正在运行的BPF程序的类型。CPU将内核正在执行任务的不同信息保存在寄存器中,借助内核提供的宏可以访问这些寄存器,如PT_REGS_RC。

程序运行结果如下:

kretprobes

相比于内核探针kprobe程序,kretprobe程序是在内核函数有返回值时插入BPF程序。当内核执行到kretprobe挂载的内核函数时,先执行内核函数,当内核函数返回时执行BPF程序,运行结束后返回。

以上面的BPF程序为例,若要使用kretprobe,可以这样修改:

b.attach_kretprobe(event="kfree_skb", fn_name="trace_kfree_skb")
  • b.attach_kretprobe()指定了该BPF程序类型为kretprobe,kretprobe类型的BPF程序将在跟踪的内核函数有返回值时执行BPF程序;
  • event="kfree_skb"指定了kretprobe挂载的内核函数为kfree_skb;
  • fn_name="trace_kfree_skb"指定了当内核函数kfree_skb有返回值时,执行程序中的trace_kfree_skb函数;

内核静态跟踪点 tracepoint

tracepoint是内核静态跟踪点,它与kprobe类程序的主要区别在于tracepoint由内核开发人员在内核中编写和修改。

tracepoint 程序的优缺点

  • 优点 跟踪点是静态的,ABI更稳定,不随内核版本的变化而致不可用。
  • 缺点 跟踪点是内核人员添加的,不会全面涵盖内核的所有子系统。

tracepoint 可用跟踪点

系统中所有的跟踪点都定义在
/sys/kernel/debug/traceing/events目录中:

使用命令perf list 也可以列出可使用的tracepoint点:

对于bcc程序来说,以监控kfree_skb为例,tracepoint程序可以这样写:

b.attach_tracepoint(tp="skb:kfree_skb", fn_name="trace_kfree_skb")

bcc遵循tracepoint命名约定,首先是指定要跟踪的子系统,这里是“skb:”,然后是子系统中的跟踪点“kfree_skb”:

总结

本文主要介绍了保证BPF程序安全的BPF验证器,然后以BPF程序的工具集BCC为例,分享了kprobes和tracepoints类型的BPF程序的使用及程序编写示例。本文分享的是内核跟踪,那么用户空间程序该如何跟踪呢,这将在后面的文章中逐步分享,感谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117794.html

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

相关文章

浏览器:理解HTTP无状态与Cookie的使用

一、理解HTTP无状态 1.1、理解http无状态 http无状态是指协议对于用户身份、用户状态、用户权限、交互场景等没有记忆能力。简单讲就是不能识别用户。 1.2、http无状态的优点&#xff1a; 可以更快地处理大量的事务&#xff0c;确保协议的可伸缩性&#xff0c;减少服务器的…

一文搞定十大排序算法(动画图解)

排序的定义 排序&#xff0c;就是重新排列表中的元素&#xff0c;使表中的元素满足按关键字递增或递减的过程。为了査找方便&#xff0c;通常要求计算机中的表是按关键字有序的。 排序的确切定义如下&#xff1a; 算法的稳定性&#xff1a; 若待排序表中有两个元素 Ri 和 Rj&am…

包装类和简单认识泛型

目录 1.包装类 1.1基本数据类型和对应的包装类 1.2装箱和拆箱 1.3自动装箱和自动拆箱 2.什么是泛型 3.引出泛型 3.1语法 4.泛型类的使用 4.1语法 4.2类型推导 5.裸类型 5.1说明 6.泛型如何编译的 6.1擦除机制 6.2为什么不能实例化泛型类型的数组 7.泛型的上界 …

MaxKey单点登录认证系统3.5.12发布,重要漏洞修复

业界领先的IAM/IDaas身份管理和认证产品 概述 MaxKey单点登录认证系统&#xff0c;谐音马克思的钥匙寓意是最大钥匙,是业界领先的IAM/IDaas身份管理和认证产品,支持OAuth 2.x/OpenID Connect、SAML 2.0、JWT、CAS、SCIM等标准协议&#xff0c;提供安全、标准和开放的用户身份…

STM32/51单片机实训day3——点亮LED灯、闪烁LED灯(二)实践

内 容&#xff1a;编写代码实现LED灯的点亮功能 学 时&#xff1a;2学时 知识点&#xff1a;分析原理图、LED灯控制原理 重点&#xff1a;GPIO参数配置、LED原理图分析 难点&#xff1a;编写 GPIO参数配置函数、LED点亮函数 时间&#xff1a;2022年12月21日 9:00&#xff5e;…

如何轻松做数据治理?开源技术栈告诉你答案

搭建一套数据治理体系耗时耗力&#xff0c;但或许我们没有必要从头开始搞自己的数据血缘项目。本文分享如何用开源、现代的 DataOps、ETL、Dashboard、元数据、数据血缘管理系统构建大数据治理基础设施。 元数据治理系统 元数据治理系统是一个提供了所有数据在哪、格式化方式…

vue2.0中集成并使用ueditor,解决第一次编辑器正常加载,第二次就不加载的问题,父组件调用子组件的方法

vue2.0中集成并使用ueditor 最近有项目需要使用编辑器&#xff0c;就找了一款百度的ueidtor编辑器&#xff0c;第一次用各种不会&#xff0c;所以记录一下 下面的链接就是ueditor的前后端的配置说明和API等 链接: ueidtor的API 下载ueidtor 下载最新版的1.5.0版本&#xf…

基于Flask+bootstrap+sqlite开发宝宝成长管理系统,宝宝成长记录系统源码

BabyLog 岁月如风&#xff0c;唯有此忆, 任凭时光匆匆&#xff0c;记录点点滴滴。 当爸爸了&#xff0c;就多陪陪孩子&#xff0c;有事没事的记些东西&#xff0c;不要总把心思放在程序编码上&#xff0c;也多陪陪孩子&#xff01; 记录了那么多条数据&#xff0c;是时候也为…

Linux之Shell脚本学习笔记(持续更新中......)

Shell是什么 命令解释器。为用户提供了一个向Linux内核发送请求一i按运行程序的界面系统级程序。 Shell编程快速入门 脚本格式要求 脚本以#!/bin/bash开头脚本需要有可执行权限 编写脚本&#xff0c;输出HelloWorld 编写 #!/bin/bash echo "hello world!"执行 …

Stm32开发1-蓝牙实现STM32的无线烧写程序 ISP模式 串口1不受影响 无线下载

最近研究如何实现Stm32的无线烧写程序。从CSDN上看到大部分的无线烧写方法&#xff0c;大都是采用ESP8266CMSIS-DAP的方式&#xff0c;其能下载也能调试程序&#xff0c;也能通过虚拟串口通信。但是其串口应该是用软件实现的&#xff0c;会占用系统资源&#xff0c;对于其能否实…

【unity3D】DoTween动画插件(上)

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于游戏开发的学习笔记 &#x1f236;本篇是unity的DoTween动画插件 DoTween动画插件&#xff08;上&#xff09;基础知识一些常用方法DOMove和DoLocalMovefrom TerrnsDOText打字机…

JavaScript篇.day04-函数, 对象类型

目录 1.函数 2.对象类型 3.例题 (1)斐波拉契数列(递归) (2)猴子吃桃(递归) (3)求100内的数之和(递归) 1.函数 函数: 是完成某个功能的代码块 函数声明 function 函数名(){ }function 函数名(形参1, 形参2...){ }函数表达式: var 函数名 function(){ } 匿名函数 …

springdata-Jpa-04原理

Repository原理 核心机制&#xff1a; 动态代理 &#xff1a; JdkDynamicAopProxy#invoke 调用JPA的Repository统一实现 SimpleJpaRepository 就是去通过JPA的API完成的持久化操作 Spring整合jpa原理 Spring怎么管理Repository( 怎么创建的Repository的Bean) 1.EnableJpaRep…

多线程与高并发(15)——线程池详解(非源码层面)

通过之前的一篇文章&#xff0c;我们总结了Executor框架。而在Executor框架中&#xff0c;ThreadPoolExecutor 是最核心的类。 ThreadPoolExecutor 看字面意思&#xff0c;是线程池的执行器。我们本篇文章就基于ThreadPoolExecutor 这个类来展开总结线程池。 下篇文章会从源码的…

java之线程同步和线程之间的通信

线程同步的概念&#xff1a; 由于同一个进程的多个线程共享同一块存储空间,在带来方便的同时,也会带来访问冲突的问题&#xff1a; 举例&#xff1a; public class Runnable_test implements Runnable {//实现Runnable接口 private int ticknumbers10;Overridepublic void …

【C++】——数据类型(二)

文章目录2. 数据类型2.1 整型2.2 sizeof关键字2.3 浮点型2.3.1 浮点数表示——小数点表示法2.3.2 浮点数表示——E表示法2.3.3 浮点数类型2.4 字符型2.5 转义字符2.6 字符串型2.7 布尔类型bool2. 数据类型 2.1 整型 整数就是没有小数部分的数字&#xff0c;如2、98、 -5286 和…

【机器学习】模型评估与选择(实战)

模型评估与选择&#xff08;实战&#xff09; 目录一、准备工作&#xff08;设置 jupyter notebook 中的字体大小样式等&#xff09;二、数据集读取与查看三、交叉验证实验1、划分数据集并置乱2、设计交叉验证实验3、进行训练&#xff08;采用随机梯度下降分类器&#xff09;4、…

推荐一款数据可视化分析工具

当今时代数据分析的发展&#xff0c;导致数据可视化成为企业必不可少的一部分&#xff0c;进而市面上也就涌现出各种丰富多彩的工具。传统的Excel无疑是数据可视化工具的典型&#xff0c;我们平时经常使用Excel制作简单表格&#xff0c;简单、方便&#xff0c;但是复杂一点的可…

酒业崛起一支奇兵,009将自信走向全球

“天若不爱酒&#xff0c;酒星不在天。地若不爱酒&#xff0c;地应无酒泉。天地既爱酒&#xff0c;爱酒不愧天。”这是李白《月下独酌》中的句子&#xff0c;诗仙也是酒仙&#xff0c;已是广为人知的轶事。 中国是酒的国度&#xff0c;酒也是历史和文化的一种表达。正因为如此&…

【读书笔记】高级FPGA设计之面积结构设计

目录 面积结构设计 折叠流水线 基于控制的逻辑复用 资源共享 复位对面积的影响 无复位的资源 无置位的资源 无同步复位的资源 复位 RAM 利用置位/复位触发器引脚 总结 面积结构设计 本篇讨论数字设计的三个主要物理特性的第二个&#xff1a;面积。并分析在FPGA中结构…