KernelSU Metamodule(元模块)完全指南:架构原理、三个 Hook 脚本开发与 meta-overlayfs 参考实现
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
Metamodule(元模块)是 KernelSU 引入的一种插件化扩展机制:它将传统 root 方案内置在守护进程核心中的模块安装/挂载逻辑,抽离为可独立安装、可替换的特殊模块。本文以官方文档为主线,结合ksud守护进程源码,系统讲解 metamodule 的设计动机、用户操作流程、hook 脚本开发规范、启动执行顺序,以及官方参考实现meta-overlayfs的架构细节,帮助普通用户正确使用、模块开发者理解兼容性边界、进阶开发者写出可上线的自定义 metamodule。
什么是 Metamodule
Metamodule 是一种特殊的 KernelSU 模块,它为整个模块系统提供核心基础设施功能。普通模块的作用是修改系统文件(通过system/目录实现 systemless 修改),而 metamodule 控制的则是普通模块如何被安装、如何被挂载。
从源码角度可以更精确地理解这一概念。metamodule.rs 中通过is_metamodule()函数判定一个模块是否为 metamodule:它读取模块的module.prop属性表,检查metamodule键的值是否为"1"或"true"(大小写不敏感)。其余所有模块都被当作普通模块处理。
关键特性:
- 基础设施角色:普通模块的运行依赖 metamodule 提供的挂载等服务;
- 单实例约束:同一时间只能安装一个 metamodule,源码在 module.rs 的安装流程中对此做了强制校验;
- 优先执行:metamodule 的脚本总是先于普通模块脚本执行(详见下文"启动执行顺序");
- 专属 Hook:提供安装(
metainstall.sh)、挂载(metamount.sh)、清理(metauninstall.sh)三个特殊脚本。
为什么需要 Metamodule
传统 root 方案把挂载逻辑内置在核心中,这使得核心更容易被检测、也更难演进。KernelSU 的 metamodule 架构通过关注点分离(separation of concerns)解决这个问题。
战略优势:
- 缩小检测面:KernelSU 自身不执行挂载操作,减少了被检测的向量;
- 稳定性:守护进程核心保持稳定,挂载实现可以独立演进、独立修复;
- 创新空间:社区可以在不 fork KernelSU 的前提下发展替代挂载策略;
- 选择自由:用户可以选择最适合自己需求的实现。
挂载方式的灵活性:
- 不挂载:对于只使用"无需挂载"型模块(仅执行脚本)的用户,完全省去挂载开销;
- OverlayFS 挂载:传统方案,支持读写层(通过
meta-overlayfs); - Magic mount:兼容 Magisk 的挂载方式,应用兼容性更好;
- 自定义实现:基于 FUSE 的 overlay、自定义 VFS 挂载,或全新的方案。
超越挂载本身的价值:
- 可扩展:无需修改 KernelSU 核心即可添加内核模块支持等新特性;
- 模块化:挂载实现可以独立于 KernelSU 版本单独更新;
- 定制化:可为特定设备或特定场景打造专用解决方案。
重要提示:如果没有安装 metamodule,普通模块将不会被挂载。全新安装的 KernelSU 必须安装一个 metamodule(如
meta-overlayfs)才能使模块生效。
用户指南
安装 Metamodule
安装 metamodule 的方式与安装普通模块完全相同:
- 下载 metamodule 的 ZIP 文件(例如
meta-overlayfs.zip); - 打开 KernelSU Manager 应用;
- 点击悬浮操作按钮(➕);
- 选择 metamodule 的 ZIP 文件;
- 重启设备。
meta-overlayfs是官方参考实现,提供基于传统 overlayfs、支持 ext4 image 的模块挂载能力。
查看当前激活的 Metamodule
在 KernelSU Manager 应用的"模块"页面可以查看当前激活的 metamodule。激活的 metamodule 会以特殊标识显示在模块列表中。
源码层面,ksud通过 metamodule.rs 中的get_metamodule_path()定位激活的 metamodule:优先解析/data/adb/metamodule符号链接指向的目录;若符号链接缺失或失效,则回退扫描/data/adb/modules/下所有模块的module.prop,查找metamodule=1的模块。
卸载 Metamodule
警告:卸载 metamodule 将影响所有模块。卸载后,所有普通模块将不再被挂载,直到安装另一个 metamodule。
卸载步骤:
- 打开 KernelSU Manager;
- 在模块列表中找到 metamodule;
- 点击卸载(此时会看到特殊警告提示);
- 确认操作;
- 重启设备。
卸载后,如果希望模块继续工作,必须安装另一个 metamodule。卸载流程对应源码 module.rs 中的处理:若卸载的是 metamodule,则调用metamodule::remove_symlink()移除/data/adb/metamodule符号链接;若卸载的是普通模块,则先执行 metamodule 提供的metauninstall.sh清理钩子。
单实例限制与切换流程
同一时间只能安装一个 metamodule。如果尝试安装第二个,KernelSU 会阻止该安装以避免冲突。安装流程中会调用 metamodule.rs 的check_install_safety(),并在检测到已有 metamodule 时直接终止安装,提示用户先卸载当前 metamodule。
切换 metamodule 的推荐流程:
- 卸载所有普通模块;
- 卸载当前的 metamodule;
- 重启;
- 安装新的 metamodule;
- 重新安装普通模块;
- 再次重启。
模块开发者视角
如果你开发的是普通 KernelSU 模块,无需为 metamodule 操心。只要用户安装了兼容的 metamodule(如meta-overlayfs),你的模块就能正常工作。
需要了解的两点:
- 挂载依赖 metamodule:模块中的
system目录只有在用户安装了提供挂载功能的 metamodule 时才会被挂载; - 无需修改代码:已有模块无需任何改动即可继续工作。
提示:如果你熟悉 Magisk 模块开发,那么安装了 metamodule 的 KernelSU 中,你的模块将以完全相同的方式工作——因为 metamodule 提供了兼容 Magisk 的挂载。
Metamodule 开发者指南
创建 metamodule 可以完全自定义 KernelSU 处理模块安装、挂载和卸载的方式。
基本要求:module.prop
metamodule 通过在module.prop中加入特殊属性来标识:
id=meta-example name=My Custom Metamodule version=1.0 versionCode=1 author=Your Name description=Custom module mounting implementation metamodule=1关键要求:
metamodule=1(或metamodule=true)属性将该模块标记为 metamodule。缺少此属性时,模块按普通模块处理——这正是 metamodule.rs 中is_metamodule()的判定逻辑;- 命名约定:强烈建议 metamodule 的 ID 以
meta-开头(如meta-overlayfs、meta-magicmount、meta-custom),便于用户识别,也避免与普通模块产生命名冲突。
文件结构
一个 metamodule 的典型目录结构:
meta-example/ ├── module.prop (必须包含 metamodule=1) │ │ *** Metamodule 专属 Hook *** ├── metamount.sh (可选:自定义挂载处理器) ├── metainstall.sh (可选:普通模块的安装钩子) ├── metauninstall.sh (可选:普通模块的清理钩子) │ │ *** 标准模块文件(均可选)*** ├── customize.sh (安装定制) ├── post-fs-data.sh (post-fs-data 阶段脚本) ├── service.sh (late_start service 阶段脚本) ├── boot-completed.sh (开机完成阶段脚本) ├── uninstall.sh (metamodule 自身的卸载脚本) └── [其他任意附加文件]metamodule 除了使用专属 hook 外,还可以使用所有标准模块特性(生命周期脚本等)。
三个 Hook 脚本详解
metamodule 最多可以提供三个特殊 hook 脚本。对应脚本文件名在 defs.rs 中定义为常量:metamount.sh、metainstall.sh、metauninstall.sh。
1. metamount.sh —— 挂载处理器
作用:控制模块在启动过程中的挂载方式。
执行时机:post-fs-data阶段,在所有模块脚本运行之前(准确顺序见下文"启动执行顺序")。源码 init_event.rs 在加载完system.prop后调用metamodule::exec_mount_script();late_load.rs 中同样会在加载流程中执行 metamodule 挂载脚本,随后进入post-mount阶段。
环境变量:
MODDIR:metamodule 的目录路径(如/data/adb/modules/meta-example);- 所有标准 KernelSU 环境变量。
从源码看,exec_mount_script()还会额外注入MODULE_DIR环境变量(指向被挂载模块的根目录),并通过 busyboxsh执行脚本。
职责:
- systemless 挂载所有已启用的模块;
- 检查
skip_mount标志; - 处理模块特殊的挂载需求。
关键要求:执行挂载操作时,必须将 source/device 名称设置为
"KSU",以此标识该挂载属于 KernelSU。
正确示例(overlay 挂载):
mount -t overlay -o lowerdir=/lower,upperdir=/upper,workdir=/work KSU /target现代 mount API 下设置 source 字符串:
fsconfig_set_string(fs, "source", "KSU")?;这一点至关重要——KernelSU 内核的 umount 逻辑与 zygisksu 的 umount 逻辑都依赖该标识才能正确卸载挂载。
示例脚本(简单 bind mount 实现):
#!/system/bin/sh MODDIR="${0%/*}" # 示例:简单 bind mount 实现 for module in /data/adb/modules/*; do if [ -f "$module/disable" ] || [ -f "$module/skip_mount" ]; then continue fi if [ -d "$module/system" ]; then # 使用 source=KSU 挂载(必需!) mount -o bind,dev=KSU "$module/system" /system fi done2. metainstall.sh —— 安装钩子
作用:定制普通模块的安装过程。
执行时机:模块安装期间,文件解压之后、安装完成之前。该脚本由内置安装器source(而非直接执行),与customize.sh的工作方式类似。
源码层面,metamodule.rs 的get_install_script()展示了其拼接逻辑:安装普通模块时,若检测到已激活的 metamodule 且存在metainstall.sh,则将其内容拼接到内置安装器脚本之后、exit 0之前组合执行;若 metamodule 被禁用(存在disable标记)或没有metainstall.sh,则回退使用默认安装器。
继承的环境变量与函数(来自内置install.sh):
- 变量:
MODPATH、TMPDIR、ZIPFILE、ARCH、API、IS64BIT、KSU、KSU_VER、KSU_VER_CODE、KSU_UAPI_VER、KSU_RUNTIME_MODE、KSU_LATE_LOAD、BOOTMODE等; - 函数:
ui_print <msg>—— 向控制台打印消息;abort <msg>—— 打印错误并终止安装;set_perm <target> <owner> <group> <permission> [context]—— 设置文件权限;set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]—— 递归设置权限;install_module—— 调用内置模块安装流程。
典型使用场景:
- 在内置安装流程前后处理模块文件(准备就绪后调用
install_module); - 移动模块文件;
- 校验模块兼容性;
- 准备特殊的目录结构;
- 初始化模块专属资源。
注意:安装 metamodule 自身时不会调用此脚本——get_install_script()对is_metamodule为真的情况直接返回默认安装器。
3. metauninstall.sh —— 清理钩子
作用:卸载普通模块时清理相关资源。
执行时机:模块卸载期间,模块目录被删除之前。源码 module.rs 的卸载流程会先调用metamodule::exec_metauninstall_script(module_id),失败仅告警不阻断卸载。
环境变量:
MODULE_ID:正在被卸载的模块 ID(由 metamodule.rs 的exec_metauninstall_script()注入)。
使用场景:
- 处理文件;
- 清理符号链接;
- 释放已分配的资源;
- 更新内部跟踪记录。
示例脚本:
#!/system/bin/sh # 卸载普通模块时被调用 MODULE_ID="$1" IMG_MNT="/data/adb/metamodule/mnt" # 从 image 中删除模块文件 if [ -d "$IMG_MNT/$MODULE_ID" ]; then rm -rf "$IMG_MNT/$MODULE_ID" fi启动执行顺序
理解启动执行顺序对 metamodule 开发至关重要。下面顺序与源码 init_event.rs 及run_stage()(init_event.rs)的实现相互印证:
post-fs-data 阶段: 1. 执行通用 post-fs-data.d 脚本 2. Prune 模块、restorecon、加载 sepolicy.rule 3. 执行 metamodule 的 post-fs-data.sh(若存在) 4. 执行普通模块的 post-fs-data.sh 5. 加载 system.prop 6. 执行 metamodule 的 metamount.sh └─> systemless 挂载所有模块 7. post-mount.d 阶段运行 - 通用 post-mount.d 脚本 - metamodule 的 post-mount.sh(若存在) - 普通模块的 post-mount.sh service 阶段: 1. 执行通用 service.d 脚本 2. 执行 metamodule 的 service.sh(若存在) 3. 执行普通模块的 service.sh boot-completed 阶段: 1. 执行通用 boot-completed.d 脚本 2. 执行 metamodule 的 boot-completed.sh(若存在) 3. 执行普通模块的 boot-completed.sh要点:
metamount.sh在所有post-fs-data 脚本(包括 metamodule 和普通模块的)之后运行;- metamodule 的生命周期脚本(
post-fs-data.sh、service.sh、boot-completed.sh)始终先于普通模块脚本执行; .d目录中的通用脚本先于 metamodule 脚本执行;post-mount阶段在挂载完成后运行。
符号链接机制
安装 metamodule 后,KernelSU 会创建符号链接:
/data/adb/metamodule -> /data/adb/modules/<metamodule_id>这为访问激活的 metamodule 提供了与 ID 无关的稳定路径。源码 metamodule.rs 的ensure_symlink()在安装时创建该链接(先清理旧的符号链接或目录再重建),卸载时由remove_symlink()移除。该路径对应 defs.rs 中的METAMODULE_DIR常量(/data/adb/metamodule/)。
收益:
- 一致的访问路径;
- 易于检测激活的 metamodule;
- 简化配置。
真实世界示例:meta-overlayfs
meta-overlayfs是官方参考实现,展示了 metamodule 开发的最佳实践。
双目录架构
meta-overlayfs采用双目录架构:
元数据目录:
/data/adb/modules/- 存放
module.prop、disable、skip_mount标记; - 启动时扫描快速;
- 存储占用小。
- 存放
内容目录:
/data/adb/metamodule/mnt/- 存放模块的实际文件(system、vendor、product 等);
- 存储在 ext4 image(
modules.img)中; - 利用 ext4 特性优化空间。
对应到源码,普通模块的system目录只有在存在且未标记skip_mount时才需要挂载(见 module.rs 中need_mount的判定:path.join("system").exists() && !path.join("skip_mount").exists()),而 metamodule 通过metamount.sh读取这些元数据并完成实际挂载。
metamount.sh 实现
以下是meta-overlayfs的挂载处理器实现方式:
#!/system/bin/sh MODDIR="${0%/*}" IMG_FILE="$MODDIR/modules.img" MNT_DIR="$MODDIR/mnt" # 若尚未挂载则挂载 ext4 image if ! mountpoint -q "$MNT_DIR"; then mkdir -p "$MNT_DIR" mount -t ext4 -o loop,rw,noatime "$IMG_FILE" "$MNT_DIR" fi # 为双目录支持设置环境变量 export MODULE_METADATA_DIR="/data/adb/modules" export MODULE_CONTENT_DIR="$MNT_DIR" # 执行挂载二进制 # (实际挂载逻辑在 Rust 二进制中) "$MODDIR/meta-overlayfs"核心特性
Overlayfs 挂载:
- 使用内核 overlayfs 实现真正的 systemless 修改;
- 支持多分区(system、vendor、product、system_ext、odm、oem);
- 通过
/data/adb/modules/.rw/提供读写层支持。
来源标识:
// 来自 meta-overlayfs/src/mount.rs fsconfig_set_string(fs, "source", "KSU")?; // 必需!这为所有 overlay 挂载设置了dev=KSU,实现正确的挂载标识与管理。
最佳实践
开发 metamodule 时请注意:
- 始终将 source 设置为 "KSU"—— 内核 umount 与 zygisksu umount 依赖此标识才能正确卸载;
- 妥善处理错误—— 启动流程对时间敏感;
- 尊重标准标志—— 支持
skip_mount和disable; - 记录操作日志—— 使用
echo或日志输出便于调试; - 充分测试—— 挂载错误可能导致开机循环(boot loop);
- 文档化行为—— 清晰说明你的 metamodule 做了什么;
- 提供迁移路径—— 帮助用户从其他方案切换过来。
测试你的 Metamodule
发布之前,建议:
- 在全新的 KernelSU 环境中测试安装;
- 用多种类型的模块验证挂载;
- 检查兼容性(与常见模块配合);
- 测试卸载与清理流程;
- 验证启动性能(注意
metamount.sh是阻塞执行的); - 确保错误处理正确,避免开机循环。
常见问题(FAQ)
我需要 metamodule 吗?
- 普通用户:仅当你想使用需要挂载的模块时才需要。如果只用只运行脚本、不修改系统文件的模块,则不需要;
- 模块开发者:不需要,你正常开发模块即可。只有当你的模块需要挂载时,用户才需要 metamodule;
- 进阶用户:仅当你想定制挂载行为或打造替代挂载实现时才需要。
可以同时安装多个 metamodule 吗?
不可以。同一时间只能安装一个 metamodule,这避免了冲突并保证行为可预测。安装流程在检测到已有 metamodule 时会拒绝安装(见 module.rs 安装逻辑)。
卸载唯一的 metamodule 会发生什么?
模块将不再被挂载。设备会正常开机,但模块的修改不会生效,直到你安装另一个 metamodule。
meta-overlayfs 是必需的吗?
不是。它提供与大多数模块兼容的标准 overlayfs 挂载。如果你需要不同的行为,完全可以创建自己的 metamodule。
参见
- 模块开发指南 —— 通用模块开发
- KernelSU 与 Magisk 的差异 —— 对比 KernelSU 与 Magisk
(另可参考仓库内印尼语原版文档 website/docs/id_ID/guide/metamodule.md,以及源码 userspace/ksud/src/metamodule.rs 与 userspace/ksud/src/init_event.rs 获取更底层的实现细节。)
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考