使用 eBPF 扩展内核

https://source.android.com/docs/core/architecture/kernel/bpf?hl=zh-cn

扩展型柏克莱封包过滤器 (eBPF) 是一个内核中的虚拟机,可运行用户提供的 eBPF 程序来扩展内核功能。这些程序可以挂接到内核中的探测点或事件,并用于收集有用的内核统计信息、监控和调试。程序使用 bpf(2) 系统调用加载到内核中,并作为 eBPF 机器指令的二进制 blob 由用户提供。Android 构建系统支持使用本文所述的简单 build 文件语法将 C 程序编译为 eBPF 程序。

如需详细了解 eBPF 内部构件和架构,请参阅 Brendan Gregg 的 eBPF 页面

Android 包含一个 eBPF 加载器和库,它可在 Android 启动时加载 eBPF 程序。

Android BPF 加载器

在 Android 启动期间,系统会加载位于 /system/etc/bpf/ 的所有 eBPF 程序。这些程序是 Android 构建系统根据 C 程序构建而成的二进制对象,并附带了 Android 源代码树中的 Android.bp 文件。构建系统将生成的对象存储在 /system/etc/bpf 中,这些对象将成为系统映像的一部分。

Android eBPF C 程序的格式

eBPF C 程序必须采用以下格式:

#include <bpf_helpers.h>

/* Define one or more maps in the maps section, for example
 * define a map of type array int -> uint32_t, with 10 entries
 */
DEFINE_BPF_MAP(name_of_my_map, ARRAY, int, uint32_t, 10);

/* this will also define type-safe accessors:
 *   value * bpf_name_of_my_map_lookup_elem(&key);
 *   int bpf_name_of_my_map_update_elem(&key, &value, flags);
 *   int bpf_name_of_my_map_delete_elem(&key);
 * as such it is heavily suggested to use lowercase *_map names.
 * Also note that due to compiler deficiencies you cannot use a type
 * of 'struct foo' but must instead use just 'foo'.  As such structs
 * must not be defined as 'struct foo {}' and must instead be
 * 'typedef struct {} foo'.
 */

DEFINE_BPF_PROG("PROGTYPE/PROGNAME", AID_*, AID_*, PROGFUNC)(..args..) {
   <body-of-code
    ... read or write to MY_MAPNAME
    ... do other things
   >
}

LICENSE("GPL"); // or other license

其中:

  • name_of_my_map 是映射变量的名称。此名称可告知 BPF 加载器要使用哪些参数来创建哪种类型的映射。此结构体定义由包含的 bpf_helpers.h 头文件提供。
  • PROGTYPE/PROGNAME 表示程序类型和程序名称。程序类型可以是下表中列出的任意类型。如果程序类型未列出,则相应程序没有严格的命名惯例;只需让连接到程序的进程知道该名称即可。

  • PROGFUNC 是一个函数,编译后将被放入所生成文件的一个区段中。

kprobe使用 kprobe 基础架构将 PROGFUNC 挂接到某个内核指令。PROGNAME 必须是 kprobe 目标内核函数的名称。如需详细了解 kprobe,请参阅 kprobe 内核文档
tracepoint将 PROGFUNC 挂接到某个跟踪点。PROGNAME 必须采用 SUBSYSTEM/EVENT 格式。例如,用于将函数附加到调度程序上下文切换事件的跟踪点区段将为 SEC("tracepoint/sched/sched_switch"),其中 sched 是跟踪子系统的名称,sched_switch 是跟踪事件的名称。如需详细了解跟踪点,请参阅跟踪事件内核文档
skfilter程序将用作网络套接字过滤器。
schedcls程序将用作网络流量分类器。
cgroupskb 和 cgroupsock只要 CGroup 中的进程创建了 AF_INET 或 AF_INET6 套接字,程序就会运行。

您可以在加载器源代码中找到更多类型。

例如,以下 myschedtp.c 程序添加了与仍在特定 CPU 上运行的最新任务 PID 相关的信息。此程序通过创建映射并定义可附加到 sched:sched_switch 跟踪事件的 tp_sched_switch 函数来实现其目标。如需了解详情,请参阅将程序附加到跟踪点

#include <linux/bpf.h>
#include <stdbool.h>
#include <stdint.h>
#include <bpf_helpers.h>

DEFINE_BPF_MAP(cpu_pid_map, ARRAY, int, uint32_t, 1024);

struct switch_args {
    unsigned long long ignore;
    char prev_comm[16];
    int prev_pid;
    int prev_prio;
    long long prev_state;
    char next_comm[16];
    int next_pid;
    int next_prio;
};

DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch)
(struct switch_args *args) {
    int key;
    uint32_t val;

    key = bpf_get_smp_processor_id();
    val = args->next_pid;

    bpf_cpu_pid_map_update_elem(&key, &val, BPF_ANY);
    return 1; // return 1 to avoid blocking simpleperf from receiving events
}

LICENSE("GPL");

当该程序使用由内核提供的 BPF 辅助函数时,系统会使用 LICENSE 宏来验证该程序是否与内核的许可证兼容。以字符串形式指定程序的许可证的名称,例如 LICENSE("GPL") 或 LICENSE("Apache 2.0")

Android.bp 文件的格式

为了使 Android 构建系统能够构建 eBPF .c 程序,您必须在项目的 Android.bp 文件中输入相应的内容。例如,如需构建一个名为 bpf_test.c 的 eBPF C 程序,请在项目的 Android.bp 文件中输入以下内容:

bpf {
    name: "bpf_test.o",
    srcs: ["bpf_test.c"],
    cflags: [
        "-Wall",
        "-Werror",
    ],
}

此内容会编译该 C 程序并生成对象 /system/etc/bpf/bpf_test.o。在启动时,Android 系统会自动将 bpf_test.o 程序加载到内核中。

注意:获得 GPL 许可证的 eBPF C 程序应采用 system/bpfprogs

sysfs 中的可用文件

在启动过程中,Android 系统会自动从 /system/etc/bpf/ 加载所有 eBPF 对象、创建程序所需的映射,并将加载的程序及其映射固定到 BPF 文件系统。这些文件随后可用于与 eBPF 程序进一步交互或读取映射。本部分介绍了这些文件的命名规范及它们在 sysfs 中的位置。

系统会创建并固定以下文件:

  • 对于加载的任何程序,假设 PROGNAME 是程序的名称,而 FILENAME 是 eBPF C 文件的名称,则 Android 加载器会创建每个程序并将其固定到 /sys/fs/bpf/prog_FILENAME_PROGTYPE_PROGNAME

    例如,对于上述 myschedtp.c 中的 sched_switch 跟踪点示例,系统会创建一个程序文件并将其固定到 /sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch

  • 对于创建的任何映射,假设 MAPNAME 是映射的名称,而 FILENAME 是 eBPF C 文件的名称,则 Android 加载器会创建每个映射并将其固定到 /sys/fs/bpf/map_FILENAME_MAPNAME

    例如,对于上述 myschedtp.c 中的 sched_switch 跟踪点示例,系统会创建一个映射文件并将其固定到 /sys/fs/bpf/map_myschedtp_cpu_pid_map

  • Android BPF 库中的 bpf_obj_get() 可从已固定的 /sys/fs/bpf 文件返回文件描述符。此文件描述符可用于进一步的操作,例如读取映射或将程序附加到跟踪点。

Android BPF 库

Android BPF 库名为 libbpf_android.so,属于系统映像的一部分。该库向用户提供了执行以下操作所需的低级别 eBPF 功能:创建和读取映射,以及创建探测点、跟踪点和性能缓冲区。

将程序附加到跟踪点

跟踪点程序会在启动时自动加载。加载跟踪点程序后,必须按照以下步骤将其激活:

  1. 调用 bpf_obj_get() 从固定文件的位置获取程序 fd。如需了解详情,请参阅 sysfs 中的可用文件
  2. 调用 BPF 库中的 bpf_attach_tracepoint(),并向其传递程序 fd 和跟踪点名称。

以下代码示例展示了如何附加上述 myschedtp.c 源文件中定义的 sched_switch 跟踪点(未显示错误检查):

  char *tp_prog_path = "/sys/fs/bpf/prog_myschedtp_tracepoint_sched_sched_switch";
  char *tp_map_path = "/sys/fs/bpf/map_myschedtp_cpu_pid";

  // Attach tracepoint and wait for 4 seconds
  int mProgFd = bpf_obj_get(tp_prog_path);
  int mMapFd = bpf_obj_get(tp_map_path);
  int ret = bpf_attach_tracepoint(mProgFd, "sched", "sched_switch");
  sleep(4);

  // Read the map to find the last PID that ran on CPU 0
  android::bpf::BpfMap<int, int> myMap(mMapFd);
  printf("last PID running on CPU %d is %d\n", 0, myMap.readValue(0));

从映射中读取数据

BPF 映射支持任意复杂的键和值结构或类型。Android BPF 库包含一个 android::BpfMap 类,该类利用 C++ 模板根据相关映射的键和值类型来实例化 BpfMap。上述代码示例演示了如何使用键和值为整数的 BpfMap。整数也可以是任意结构。

因此,使用模板化的 BpfMap 类可让您轻松定义适合特定映射的自定义 BpfMap 对象。之后可以使用所生成的自定义函数来访问该映射,这些函数为类型感知型函数,可以令代码更简洁。

如需详细了解 BpfMap,请参阅 Android 源代码

调试问题

在启动期间,系统将记录与 BPF 加载相关的多条消息。如果加载进程因任何原因失败,logcat 中会提供详细的日志消息。按照“bpf”过滤 logcat 日志时,会输出加载过程中的所有消息和错误详情,例如 eBPF 验证程序错误。

Android 中的 eBPF 示例

AOSP 中的以下程序提供使用 eBPF 的其他示例:

  • netd eBPF C 程序可供 Android 中的网络守护程序 (netd) 用于多种用途,例如过滤套接字和收集统计信息。如需了解此程序的使用方式,请查阅 eBPF 流量监控源代码。

  • time_in_state eBPF C 程序可计算 Android 应用在不同 CPU 频率下运行所花费的时间,该时间可用于计算功耗。

  • 在 Android 12 中,gpu_mem eBPF C 程序会跟踪每个进程和整个系统的总 GPU 内存用量。此程序可用于 GPU 内存性能分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
4S店客户管理小程序-毕业设计,基于微信小程序+SSM+MySql开发,源码+数据库+论文答辩+毕业论文+视频演示 社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非常有意义的。 本文从管理员、用户的功能要求出发,4S店客户管理系统中的功能模块主要是实现管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理,用户客户端:首页、车展、新闻头条、我的。门店客户端:首页、车展、新闻头条、我的经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与4S店客户管理系统实现的实际需求相结合,讨论了微信开发者技术与后台结合java语言和MySQL数据库开发4S店客户管理系统的使用。 关键字:4S店客户管理系统小程序 微信开发者 Java技术 MySQL数据库 软件的功能: 1、开发实现4S店客户管理系统的整个系统程序; 2、管理员服务端;首页、个人中心、用户管理、门店管理、车展管理、汽车品牌管理、新闻头条管理、预约试驾管理、我的收藏管理、系统管理等。 3、用户客户端:首页、车展、新闻头条、我的 4、门店客户端:首页、车展、新闻头条、我的等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流信息的查看及回复相应操作。
现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此微信小程序医院挂号预约系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。微信小程序医院挂号预约系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,医生信息管理,医院信息管理,科室信息管理,预约信息管理,预约取消管理,留言板,系统管理。微信小程序用户可以注册登录,查看医院信息,查看医生信息,查看公告资讯,在科室信息里面进行预约,也可以取消预约。微信小程序医院挂号预约系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值