动态控制eBPF程序加载:检查 Tracepoint、Kprobe是否存在

前言

在 eBPF 程序开发中,确保程序能够在各种不同的系统配置中兼容运行是至关重要的。本文将详细介绍一个方案,通过动态检查Tracepoint、Kprobe是否存在,并结合libbpf的API接口控制 eBPF 程序的加载。这种方法不仅可以提升程序的灵活性,还能提高其在不同内核版本和系统配置中的兼容性。

方案概述

本方案包括以下步骤:

  1. 检查指定的 Tracepoint 是否存在;
  2. 检查指定的 Kprobe 是否存在;
  3. 若不存在,则通过API函数bpf_object__find_program_by_name查找到 eBPF 程序,再使用 API函数bpf_program__set_autoload 动态设置 eBPF 程序的autoload属性使其不加载。

检查Tracepoint挂载点是否存在

Tracepoint是内核提供的一种跟踪机制,通过访问特定目录可以检查其是否存在。不同的Linux系统,查找的目录不同,当前有以下两种路径:

  1. /sys/kernel/debug/tracing/events
  2. /sys/kernel/tracing/events

设计思路是顺序地在这两个路径下使用access系统调用检查是否存在某tracepoint文件,任意一个路径下检查到了,结果就是存在;两个路径下都没检查到,则结果是不存在。
注:这两个路径一般需要root权限,普通用户无法访问。

示例代码

#include <iostream>
#include <unistd.h>bool tracepoint_exists(const char* tp_category, const char* tp_name)
{char path[256];snprintf(path, sizeof(path), "/sys/kernel/debug/tracing/events/%s/%s",tp_category, tp_name);auto ret = access(path, F_OK);if (ret == 0) {return true;}snprintf(path, sizeof(path), "/sys/kernel/tracing/events/%s/%s", tp_category, tp_name);ret = access(path, F_OK);if (ret == 0) {return true;}return false;
}int main(int argc, char** argv)
{if (argc != 3) {std::cout << "Usage: ./test category tracepoint_name" << std::endl;return -1;}auto ret = tracepoint_exists(argv[1], argv[2]);if (ret) {std::cout << "Tracepoint tp/" << argv[1] << "/" << argv[2]<< " exists!"<< std::endl;return 0;}std::cout << "Tracepoint tp/" << argv[1] << "/" << argv[2]<< " does not exist!"<< std::endl;return -1;
}

测试结果

使用以上程序检测syscalls:sys_enter_read是否存在。

[root@VM-8-2-centos check_tracepoint_kprobe_uprobe]# ./test syscalls sys_enter_read
Tracepoint tp/syscalls/sys_enter_read exists!

再检测一个不存在的,syscalls:sys_enter_readd。

[root@VM-8-2-centos check_tracepoint_kprobe_uprobe]# ./test syscalls sys_enter_readd
Tracepoint tp/syscalls/sys_enter_readd does not exist!

通过测试结果可知,功能符合预期。

检查Kprobe挂载点是否存在

Kprobes允许动态挂载内核函数进行调试,通过读取以下系统文件可以检查特定的 Kprobe 是否存在。

  1. /sys/kernel/debug/tracing/available_filter_functions
  2. /sys/kernel/tracing/available_filter_functions

注:这两个文件一般需要root权限,普通用户无法访问。

示例代码

#include <iostream>
#include <stdio.h>
#include <string.h>bool kprobe_exists(const char *kprobe_name)
{FILE* file = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r");if (!file) {file = fopen("/sys/kernel/tracing/available_filter_functions", "r");if (!file) {return false;}}char line[256];while (fgets(line, sizeof(line), file)) {line[strcspn(line, "\n")] = 0;if (strcmp(line, kprobe_name) == 0) {return true;}}return false;
}int main(int argc, char** argv)
{if (argc != 2) {std::cout << "Usage: ./test kprobe_name" << std::endl;return -1;}auto ret = kprobe_exists(argv[1]);if (ret) {std::cout << "kprobe " << argv[1] << " exists!"<< std::endl;return 0;}std::cout << "kprobe " << argv[1] << " does not exist!"<< std::endl;return -1;
}

测试结果

使用以上程序检测__alloc_pages_direct_compact这个kprobe挂载点是否存在。

[root@VM-8-2-centos check_kprobe]# ./test __alloc_pages_direct_compact
kprobe __alloc_pages_direct_compact exists!

再检测一个不存在的,__alloc_pages_direct_compact_abcdefg。

[root@VM-8-2-centos check_kprobe]# ./test __alloc_pages_direct_compact_abcdefg
kprobe __alloc_pages_direct_compact_abcdefg does not exist!

通过测试结果可知,功能符合预期。

动态设置 eBPF 程序的 autoload 属性

当我们以libbpf-bootstrap为参考写ebpf程序时,可以使用前文的方法提前判断函数挂载点是否存在,若不存在,则使用libbpf的API将eBPF 程序的 autoload 属性设置为false。使用到的API如下:

bpf_object__find_program_by_name

bpf_object__find_program_by_name接口是一个用于在 eBPF 对象中查找特定 eBPF 程序的函数,在bpf/libbpf.h头文件中。
函数功能
在 eBPF 项目中,一个 BPF 对象可能包含多个 eBPF 程序(如一个bpf c语言源文件中有多个SEC),每个程序都可以有一个唯一的名称。bpf_object__find_program_by_name接口的主要功能是根据给定的程序名称,从 BPF 对象中查找并返回相应的 eBPF 程序。如果找到匹配的程序,则返回指向该程序的指针;否则,返回 NULL。
在前文中,我们能够通过程序动态判断是否支持某Tracepoint或Kprobe的挂载点,若不存在,需要使用这个API查找到对应的ebpf程序(struct bpf_program *),以便后续设置其autoload属性。
函数原型

struct bpf_program * bpf_object__find_program_by_name(const struct bpf_object *obj, const char *name);

参数说明

  1. obj: 指向 BPF 对象的指针。该对象通常是通过加载 BPF ELF 文件生成的,包含多个 eBPF 程序。在libbpf-bootstrap框架中,可以通过skel->obj来获取。
  2. name: 要查找的 eBPF 程序的名称。名称是一个字符串,应与 BPF 程序在源代码中的定义名称相匹配。

返回值
成功时,返回指向找到的 eBPF 程序的指针(struct bpf_program *);
如果未找到匹配的程序,则失败,返回 NULL。

bpf_program__set_autoload

bpf_program__set_autoload接口定义在bpf/libbpf.h头文件中,用于设置 eBPF 程序在加载 BPF 对象时是否自动加载。这在需要根据运行时条件动态控制 eBPF 程序的加载和执行时非常有用。
函数功能
bpf_program__set_autoload 接口的主要功能是启用或禁用特定 eBPF 程序的自动加载。通过调用此函数,开发者可以决定某个 eBPF 程序在调用 bpf_object__load 时是否应被加载到内核中。
函数原型

int bpf_program__set_autoload(struct bpf_program *prog, bool autoload);

参数说明

  1. prog: 指向要设置自动加载属性的 eBPF 程序的指针(struct bpf_program *)。
  2. autoload: 一个布尔值,指示是否应自动加载该程序。设为 true 表示启用自动加载,设为 false 表示禁用自动加载。

返回值
成功时返回 0;
失败时返回一个负值的错误码。

代码示例

以下代码展示了如何使用前文的两个API实现动态禁用某ebpf程序的功能。

void disable_autoload(struct bpf_object *obj, const char *prog_name) {struct bpf_program *prog = bpf_object__find_program_by_name(obj, prog_name);if (!prog) {fprintf(stderr, "Program %s not found in object\n", prog_name);return;}if (bpf_program__set_autoload(prog, false) != 0) {fprintf(stderr, "Failed to disable autoload for program %s\n", prog_name);} else {printf("Autoload disabled for program %s\n", prog_name);}
}

完整的动态禁用eBPF程序autoload属性的小demo可免费下载资源《禁用某eBPF程序源代码 》。Demo中在eBPF程序的open和load之间调用了disable_autoload,将不存在的tracepoint禁用了。

结束语

通过本文的学习,在开发eBPF程序时可以实现对 eBPF 程序加载的动态控制,确保其在不同的系统配置和内核版本中都能正常运行。我们详细探讨了如何检查 Tracepoint、Kprobe是否存在,并结合 bpf_program__set_autoload 和 bpf_object__find_program_by_name 控制 eBPF 程序的加载。这不仅提升了 eBPF 程序的灵活性,还增强了系统兼容性。希望本文的内容能为 eBPF 程序开发提供有价值的指导,帮助读者更有效地利用 eBPF 技术实现内核级监控和调试。
后续会对Uprobe和USDT的检测进行研究,欢迎关注和订阅。

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

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

相关文章

jwt 实现用户登录完整java

登录校验逻辑 用户登录的校验逻辑分为三个主要步骤&#xff0c;分别是校验验证码&#xff0c;校验用户状态和校验密码&#xff0c;具体逻辑如下 前端发送username、password、captchaKey、captchaCode请求登录。判断captchaCode是否为空&#xff0c;若为空&#xff0c;则直接…

AWS联网和内容分发服务

概况 VPC Amazon Virtual Private Cloud (Amazon VPC) 让您能够全面地控制自己的虚拟网络环境&#xff0c;包括资源放置、连接性和安全性。首先在 AWS 服务控制台中设置 VPC。然后&#xff0c;向其中添加资源&#xff0c;例如 Amazon Elastic Compute Cloud (EC2) 和 Amazon …

数据分析必备:一步步教你如何用Pandas做数据分析(15)

1、Pandas 数据丢失 Pandas 数据丢失的操作实例 在现实生活中&#xff0c;数据丢失始终是一个问题。机器学习和数据挖掘等领域在模型预测的准确性方面面临严重问题&#xff0c;因为缺少值会导致数据质量较差。在这些领域中&#xff0c;缺失值处理是使模型更准确和有效的主要重…

定个小目标之每天刷LeetCode热题(7)

今天这道题是道简单题&#xff0c;使用双指针进行迭代即可&#xff0c;画了下草图如下 代码如下 class Solution {public ListNode reverseList(ListNode head) {if (head null || head.next null) {return head;}ListNode p head, q head.next, temp null;while (q ! nu…

【Python如何将EXCEL拆分】

文章目录 Python将一个EXCEL表拆分多个excel表Python将一个EXCEL表中一个sheet拆分多个sheet表 Python将一个EXCEL表拆分多个excel表 在Python中&#xff0c;你可以使用pandas库来读取Excel文件&#xff0c;并将一个大的Excel表格&#xff08;工作表&#xff09;拆分成多个单独…

Writerside生成在线帮助文档或用户手册软件基础使用教程

Writerside是JetBrains出的一个技术文档工具&#xff0c;既能用在JetBrains IDE上&#xff0c;也能单独用。它能帮你轻松写、建、测、发技术文档&#xff0c;像产品说明、API参考、开发指南等都能搞定。 特点&#xff1a; 文档即代码&#xff1a;它让你像管代码一样管文档&…

【大数据Spark】常见面试题(万字!建议收藏)

文章目录 入门级中等难度中高级难度数据倾斜解决方法 入门级 什么是Apache Spark&#xff1f;它与传统的MapReduce有何不同&#xff1f; Apache Spark是一个开源的分布式计算系统&#xff0c;它提供了高效的数据处理和分析能力。与传统的MapReduce相比&#xff0c;Spark具有更快…

海光CPU:国产信创的“芯“动力解读

国产信创CPU-海光CPU CPU&#xff1a;信创根基&#xff0c;国之重器 国产CPU形成三大阵营&#xff1a;自主架构、x86及ARM。自主阵营中&#xff0c;龙芯和申威以LoongArch和SW-64为基石&#xff1b;ARM阵营由鲲鹏、飞腾主导&#xff0c;依托ARM授权研发处理器&#xff1b;x86阵…

红帽练习 之逻辑卷 pv lv gv

逻辑卷习题 1 在/dev/sdb 存储设备上创建物理设备分区 创建2个大小各为256MB的分区 并设置为linux lvm类型 使用first 和second 作为这些分区的名称 parted /dev/sdb mklabel gpt parted /dev/sdb primary mkpart first 1M 256M parted /dev/sdb set 1 …

【Linux|数据恢复】extundelete和ext4magic数据恢复工具使用

环境&#xff1a;Centos7.6_x86 一、extundelete工具 1、extundelete介绍 Extundelete 是一个数据恢复工具&#xff0c;用于从 ext3 或 ext4 分区中恢复删除文件。根据官网0.2.4版本介绍是支持ext4&#xff0c;但实际上使用发现ext4格式有些问题&#xff0c;会报以下错误&…

动态SQL IF语句

IF语句学习 第一种写法(标准) 我们先来看以下标准写法: select * from .. <where> <if test""> and ....... <if test""> and ....... <where> 我们用了一个where标签 , 内嵌if语句 第二种写法: 这是第二种写法:不用where标…

大降分!重邮计算机专硕复试线大降50分!重庆邮电计算机考研考情分析!

重庆邮电大学&#xff08;Chongqing University of Posts and Telecommunications&#xff09;简称重邮&#xff0c;坐落于中国重庆市主城区南山风景区内&#xff0c;是中华人民共和国工业和信息化部与重庆市人民政府共建的教学研究型大学&#xff0c;入选国家“中西部高校基础…

一篇文章搞懂Go语言切片底层原理(图文并茂+举例讲解)

1. 切片和数组的底层关系 Go语言切片的数据结构是一个结构体&#xff1a; type slice struct {array unsafe.Pointerlen intcap int }Go语言中切片的内部结构包含地址、大小和容量。将数组比喻成一个蛋糕&#xff0c;那么切片就是需要切的那一块&#xff0c;而那一块的的…

c++学生管理系统

想要实现的功能 1&#xff0c;可以增加学生的信息&#xff0c;包括&#xff08;姓名&#xff0c;学号,c成绩&#xff0c;高数成绩&#xff0c;英语成绩&#xff09; 2&#xff0c;可以删除学生信息 3&#xff0c;修改学生信息 4&#xff0c;显示所有学生信息 5&#xff0c…

支持AMD GPU的llm.c

anthonix/llm.c: LLM training in simple, raw C/HIP for AMD GPUs (github.com) llm.c for AMD devices This is a fork of Andrej Karpathys llm.c with support for AMD devices. 性能 在单个7900 XTX显卡上使用默认设置&#xff0c;目前的训练步骤耗时约为79毫秒&#x…

Docker的安装、启动和配置镜像加速

前言&#xff1a; Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 而企业部署一般都是采用Linux操作系统&#xff0c;而…

【软件设计师】2022年上半年真题解析

​​冯诺依曼计算机体系结构的基本特点是&#xff1a; A. 程序指令和数据都采用二进制表示 - 这是正确的&#xff0c;因为冯诺依曼架构下的计算机使用二进制形式来表示和处理所有信息&#xff0c;包括指令和数据。 B. 程序指令总是存储在主存中&#xff0c;而数据则存储在高速…

Java基础语法详解——入门学习教程

Java 基础 目录 一、数据类型 基本类型包装类型缓存池 二、String 概览不可变的好处String, StringBuffer and StringBuilder String Poolnew String(“abc”) 三、运算 参数传递float 与 double隐式类型转换switch 四、关键字 finalstatic 五、Object 通用方法 概览equals()ha…

深入解析 MongoDB Map-Reduce:强大数据聚合与分析的利器

Map-Reduce 是一种用于处理和生成大数据集的方法&#xff0c;MongoDB 支持 Map-Reduce 操作以执行复杂的数据聚合任务。Map-Reduce 操作由两个阶段组成&#xff1a;Map 阶段和 Reduce 阶段。 基本语法 在 MongoDB 中&#xff0c;可以使用 db.collection.mapReduce() 方法执行…

IsoBench:多模态基础模型性能的基准测试与优化

随着多模态基础模型的快速发展&#xff0c;如何准确评估这些模型在不同输入模态下的性能成为了一个重要课题。本文提出了IsoBench&#xff0c;一个基准数据集&#xff0c;旨在通过提供多种同构&#xff08;isomorphic&#xff09;表示形式的问题&#xff0c;来测试和评估多模态…