website/docs/zh_TW/guide/module.md
KernelSU 提供了一個模組機制,它可以在保持系統分割區完整性的同時達到修改系統分割區的效果;這種機制一般被稱為 systemless (無系統修改)。
KernelSU 的模組運作機制與 Magisk 幾乎相同,如果您熟悉 Magisk 模組的開發,那麼開發 KernelSU 的模組大同小異,您可以跳過下列有關模組的介紹,只需要瞭解 KernelSU 模組與 Magisk 模組的差異。
::: warning METAMODULE 僅在修改系統檔案時需要
KernelSU 使用 metamodule 架構來掛載 system 目錄。僅當您的模組需要修改 /system 檔案時(透過 system 目錄),您才需要安裝 metamodule(如 meta-overlayfs)。其他模組功能如腳本、sepolicy 規則和 system.prop 無需 metamodule 即可運作。
:::
KernelSU 的模組支援顯示互動介面,請參閱 WebUI 文檔.
KernelSU 提供了一個內建的配置系統,允許模組儲存持久化或暫時的鍵值設定。詳情請參閱模組配置文檔。
KernelSU 提供了一個完備的 BusyBox 二進位檔案 (包括完整的 SELinux 支援)。可執行檔位於 /data/adb/ksu/bin/busybox。
KernelSU 的 BusyBox 支援執行時可切換的 "ASH 獨立模式"。
ASH 獨立模式在執行 BusyBox 時,每個命令都會直接使用 BusyBox 中內建的應用程式,而不論 PATH 的設定為何。
例如,ls、rm、chmod 等命令將不會使用 PATH 中設定的命令 (在 Android 的狀況下,預設狀況下分別為 /system/bin/ls、/system/bin/rm 和 /system/bin/chmod),而是直接呼叫 BusyBox 內建的應用程式。
這確保了腳本始終在可預測的環境中執行,並始終具有完整的命令套件,不論它執行在哪個 Android 版本上。
要強制下一個命令不使用 BusyBox,您必須使用完整路徑呼叫可執行檔。
每個基於 KernelSU 上下文的腳本都將在 BusyBox 的獨立模式執行。對於第三方開發人員而言,這包括所有開機腳本和模組安裝腳本。
對於想要在 KernelSU 之外使用這個「獨立模式」功能的使用者,有兩種啟用方法:
ASH_STANDALONE 設為 1。例如:ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>/data/adb/ksu/bin/busybox sh -o standalone <script>為了確保所有後續的 sh shell 都在獨立模式下執行,第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理器內部使用的方法),因為環境變數會被繼承到子執行緒中。
::: tip 與 Magisk 的差異 KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案,感謝 Magisk! 因此,您完全不必擔心 BusyBox 腳本與在 Magisk 和 KernelSU 之間的相容性問題,因為它們完全相同! :::
KernelSU 模組是一個放置於 /data/adb/modules 且滿足下列結構的資料夾:
/data/adb/modules
├── .
├── .
|
├── $MODID <--- 模組的資料夾名稱與模組 ID 相同
│ │
│ │ *** 模組識別 ***
│ │
│ ├── module.prop <--- 這個檔案儲存與模組相關的中繼資料,例如模組 ID、版本等
│ │
│ │ *** 主要內容 ***
│ │
│ ├── system <--- 這個資料夾會在 skip_mount 不存在時被掛接至系統
│ │ ├── ...
│ │ ├── ...
│ │ └── ...
│ │
│ │ *** 狀態旗標 ***
│ │
│ ├── skip_mount <--- 如果這個檔案存在,那麼 KernelSU 將不會掛接您的系統資料夾
│ ├── disable <--- 如果這個檔案存在,那麼模組將會被停用
│ ├── remove <--- 如果這個檔案存在,那麼模組將會在下次重新開機時被移除
│ │
│ │ *** 選用檔案 ***
│ │
│ ├── post-fs-data.sh <--- 這個腳本將會在 post-fs-data 中執行
│ ├── service.sh <--- 這個腳本將會在 late_start 服務中執行
| ├── uninstall.sh <--- 這個腳本將會在 KernelSU 移除模組時執行
│ ├── system.prop <--- 這個檔案中指定的屬性將會在系統啟動時透過 resetprop 變更
│ ├── sepolicy.rule <--- 這個檔案中的 SELinux 原則將會在系統開機時載入
│ │
│ │ *** 自動產生的目錄,不要手動建立或修改! ***
│ │
│ ├── vendor <--- A symlink to $MODID/system/vendor
│ ├── product <--- A symlink to $MODID/system/product
│ ├── system_ext <--- A symlink to $MODID/system/system_ext
│ │
│ │ *** 允許的其他額外檔案/資料夾 ***
│ │
│ ├── ...
│ └── ...
|
├── another_module
│ ├── .
│ └── .
├── .
├── .
::: tip 與 Magisk 的差異 KernelSU 沒有內建的針對 Zygisk 的支援,因此模組中沒有與 Zygisk 相關的內容,但您可以透過安裝 ZygiskNext 以支援 Zygisk 模組,此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。 :::
module.prop 是一個模組的設定檔,在 KernelSU 中如果模組中不包含這個檔案,那麼它將不被認為是一個模組;這個檔案的格式如下:
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
updateJson=<url> (optional)
actionIcon=<path> (optional)
webuiIcon=<path> (optional)
^[a-zA-Z][a-zA-Z0-9._-]+$ 例如:✓ a_module,✓ a.module,✓ module-101,✗ a module,✗ 1_module,✗ -a-module。這是您的模組的唯一識別碼,發表後將無法變更。UNIX (LF) 分行符號類型,而非 Windows (CR + LF) 或 Macintosh (CR)。actionIcon=icon/icon.png
會解析為 <MODDIR>/icon/icon.png。::: tip 動態描述
description 欄位可以在執行階段使用模組配置系統動態覆蓋。詳情請參閱覆蓋模組描述。
:::
請閱讀 開機腳本 章節,以瞭解 post-fs-data.sh 和 service.sh 之間的差別。對於大多數模組開發人員來說,如果您只需要執行一個開機腳本,service.sh 應該已經足夠了。
如果您需要在啟動完成後執行腳本,請使用 boot-completed.sh。
如果你想在掛載 overlayfs 後做一些事情,請使用 post-mount.sh。
在您的模組中的所有腳本中,請使用 MODDIR=${0%/*} 以取得您的模組基本目錄路徑;請不要在腳本中以硬式編碼的方式加入您的模組路徑。
:::tip 與 Magisk 的差異
您可以透過環境變數 KSU 來判斷腳本是執行在 KernelSU 還是 Magisk 中,如果執行在 KernelSU,這個值會被設為 true。
:::
system 目錄 {#system-directories}這個目錄的內容會在系統啟動後,以 overlayfs 的方式覆疊在系統的 /system 分區之上,這表示:
如果您想要刪除系統先前的目錄中的某個檔案或資料夾,您需要在模組目錄中透過 mknod filename c 0 0 以建立一個 filename 的相同名稱的檔案;這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (/system 分割區並未被變更)。
您也可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行 mknod <TARGET> c 0 0。例如:
REMOVE="
/system/app/YouTube
/system/app/Bloatware
"
上方的清單將會執行:mknod $MODPATH/system/app/YouTuBe c 0 0 和 mknod $MODPATH/system/app/Bloatware c 0 0;並且 /system/app/YouTube 和 /system/app/Bloatware 將會在模組生效前移除。
如果您想要取代系統的某個目錄,您需要在模組目錄中建立一個相同路徑的目錄,然後為此目錄設定此屬性:setfattr -n trusted.overlay.opaque -v y <TARGET>;這樣 overlayfs 系統會自動將對應目錄取代 (/system 分割區並未被變更)。
您可以在 customize.sh 中宣告一個名為 REMOVE 並且包含一系列目錄的變數以執行移除作業,KernelSU 會自動為您在模組對應目錄執行相關作業。例如:
REPLACE="
/system/app/YouTube
/system/app/Bloatware
"
上方的清單將會執行:自動建立目錄 $MODPATH/system/app/YouTube 和 $MODPATH//system/app/Bloatware,然後執行 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/YouTube 和 setfattr -n trusted.overlay.opaque -v y $$MODPATH/system/app/Bloatware;並且 /system/app/YouTube 和 /system/app/Bloatware 將會在模組生效後被取代為空白目錄。
::: tip 與 Magisk 的差異
KernelSU 的 systemless 機制透過核心的 overlayfs 實作,而 Magisk 目前則是透過 magic mount (bind mount),兩者的實作方式有很大的差別,但最終的目標是一致的:不修改實際的 /system 分區但修改 /system 檔案。
:::
如果您對 overlayfs 感興趣,建議閱讀 Linux Kernel 關於 overlayfs 的文檔
這個檔案的格式與 build.prop 完全相同:每一行都是由 [key]=[value] 組成。
如果您的模組需要一些額外 sepolicy 修補,請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。
KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案,這個 Zip 檔案的格式如下:
module.zip
│
├── customize.sh <--- (Optional, more details later)
│ This script will be sourced by update-binary
├── ...
├── ... /* 其他模块文件 */
│
:::warning 警告 KernelSU 模組不支援在 Recovery 中安裝!! :::
如果您想要控制模組的安裝過程,可以在模組的目錄下建立一個名為 customize.sh 的檔案,這個檔案將會在模組被解壓縮後經由 source 調用,如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業,這個腳本將非常有用。
如果您想完全控制腳本的安裝過程,您可以在 customize.sh 中宣告 SKIPUNZIP=1 以跳過所有的預設安裝步驟。此時,您需要自行處理所有的安裝過程(例如解壓縮模組、設定權限等)。
customize.sh 腳本以「獨立模式」執行在 KernelSU 的 BusyBox ash shell 中。您可以使用下列變數和函式:
KSU (bool): 標示此腳本執行於 KernelSU 環境中,此變數的值將永遠為 true,您可以透過它與 Magisk 進行區分。KSU_VER (string): KernelSU 目前的版本名稱 (例如 v0.4.0)KSU_VER_CODE (int): KernelSU 使用者空間目前的版本代碼 (例如 10672)KSU_KERNEL_VER_CODE (int): KernelSU 核心空間目前的版本代碼 (例如 10672)BOOTMODE (bool): 此變數在 KernelSU 中永遠為 trueMODPATH (path): 目前模組的安裝目錄TMPDIR (path): 可以存放暫存檔的位置ZIPFILE (path): 目前模組的安裝程式 ZipARCH (string): 裝置的 CPU 架構,有這幾種:arm, arm64, x86, or x64IS64BIT (bool): 是否為 64 位元裝置API (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 23)::: warning 警告
MAGISK_VER_CODE 在 KernelSU 永遠為 25200,MAGISK_VER 則為 v25.2,請不要透過這兩個變數來判斷是否為 KernelSU!
:::
ui_print <msg>
print <msg> to console
Avoid using 'echo' as it will not display in custom recovery's console
abort <msg>
print error message <msg> to console and terminate the installation
Avoid using 'exit' as it will skip the termination cleanup steps
set_perm <target> <owner> <group> <permission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
this function is a shorthand for the following commands:
chown owner.group target
chmod permission target
chcon context target
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
if [context] is not set, the default is "u:object_r:system_file:s0"
for all files in <directory>, it will call:
set_perm file owner group filepermission context
for all directories in <directory> (including itself), it will call:
set_perm dir owner group dirpermission context
在 KernelSU 中,依據腳本執行模式的不同分為兩種:post-fs-data 模式和 late_start 服務模式。
post-fs-data 模式
setprop 會導致開機程序死鎖!請使用 resetprop -n <prop_name> <prop_value> 替代。late_start 服務模式
在 KernelSU 中,開機腳本依據存放位置的不同還分為兩種:一般腳本和模組腳本。
一般腳本
/data/adb/post-fs-data.d 或 /data/adb/service.d 中。chmod +x script.sh) 時才會被執行。post-fs-data.d 中的腳本以 post-fs-data 模式執行,在 service.d 中的腳本以 late_start 服務模式執行。模組腳本
post-fs-data.sh 以 post-fs-data 模式運行,post-mount.sh 在 overlayfs 掛載後運行,而 service.sh 則以 late_start 服務模式運行,boot-completed 在 Android 系統啟動完畢後以服務模式運作。所有啟動腳本都將在 KernelSU 的 BusyBox ash shell 中執行,並啟用獨立模式。
以下是Android的相關啟動流程(部分省略),其中包括KernelSU的運行(帶前導星號),可以幫助您更好地理解這些模組腳本的用途:
0. Bootloader (nothing on screen)
load patched boot.img
load kernel:
- GKI mode: GKI kernel with KernelSU integrated
- LKM mode: stock kernel
...
1. kernel exec init (oem logo on screen):
- GKI mode: stock init
- LKM mode: exec ksuinit, insmod kernelsu.ko, exec stock init
mount /dev, /dev/pts, /proc, /sys, etc.
property-init -> read default props
read init.rc
...
early-init -> init -> late_init
early-fs
start vold
fs
mount /vendor, /system, /persist, etc.
post-fs-data
*safe mode check
*execute general scripts in post-fs-data.d/
*load sepolicy.rule
*mount tmpfs
*execute module scripts post-fs-data.sh
**(Zygisk)./bin/zygisk-ptrace64 monitor
*(pre)load system.prop (same as resetprop -n)
*remount modules /system
*execute general scripts in post-mount.d/
*execute module scripts post-mount.sh
zygote-start
load_all_props_action
*execute resetprop (actual set props for resetprop with -n option)
... -> boot
class_start core
start-service logd, console, vold, etc.
class_start main
start-service adb, netd (iptables), zygote, etc.
2. kernel2user init (rom animation on screen, start by service bootanim)
*execute general scripts in service.d/
*execute module scripts service.sh
*set props for resetprop without -p option
**(Zygisk) hook zygote (start zygiskd)
**(Zygisk) mount zygisksu/module.prop
start system apps (autostart)
...
boot complete (broadcast ACTION_BOOT_COMPLETED event)
*execute general scripts in boot-completed.d/
*execute module scripts boot-completed.sh
3. User operable (lock screen)
input password to decrypt /data/data
*actual set props for resetprop with -p option
start user apps (autostart)
如果您對 Android 的 Init 語言感興趣,建議閱讀它的 文檔.
除了上述標準啟動流程外,KernelSU 還支援 late-load 模式,用於 LKM(可載入核心模組)場景。在該模式下,KernelSU 核心模組在系統完全啟動後載入,而非在 init 過程中載入。
透過執行 ksud late-load 命令觸發。該命令會:
kernelsu.ko。由於系統已經完全運行,某些啟動時的機制不可用或不需要。
| 行為 | 標準啟動 | Late-load 模式 |
|---|---|---|
| 核心模組由 init (PID 1) 載入 | 是 | 否(啟動後載入) |
| ksud 的 kprobe 鉤子 (execve/read/fstat/input) | 是 | 跳過 |
| 安全模式偵測(音量鍵) | 是 | 始終停用 |
| 啟動日誌擷取 (logcat/dmesg) | 是 | 跳過 |
| Magisk 共存偵測 | 是 | 跳過 |
post-fs-data 事件通知核心 | 是 | 跳過 |
boot-completed 事件通知核心 | 是 | 初始化時直接設定 |
post-fs-data.sh / post-fs-data.d/ 指令碼 | 是 | 由 late-load 階段替代 |
system.prop 載入 | 是 | 是 |
| OverlayFS 掛載(metamodule) | 是 | 是 |
post-mount.sh / post-mount.d/ 指令碼 | 是 | 是 |
service.sh / service.d/ 指令碼 | 是 | 是 |
boot-completed.sh / boot-completed.d/ 指令碼 | 是 | 是 |
KSU_LATE_LOAD 環境變數 | 未設定 | 設定為 1 |
核心 info 標誌位 0x4 | 未設定 | 已設定 |
在 late-load 模式下,指令碼執行順序如下:
ksud late-load:
1. 載入 kernelsu.ko(如果尚未載入)
2. 釋放二進位檔案、處理模組更新、載入 SELinux 規則、初始化 feature
3. 執行 late-load.d/ 通用指令碼和模組的 late-load 指令碼(阻塞)
4. 載入 system.prop (resetprop -n)
5. 執行 metamodule mount 指令碼(OverlayFS 掛載)
6. 執行 post-mount.d/ 通用指令碼和模組的 post-mount.sh(阻塞)
7. 執行 service.d/ 通用指令碼和模組的 service.sh(非阻塞)
8. 執行 boot-completed.d/ 通用指令碼和模組的 boot-completed.sh(非阻塞)
模組可以提供 late-load.sh 指令碼,該指令碼僅在 late-load 模式下執行,作為 post-fs-data.sh 的替代。該指令碼在 OverlayFS 掛載之前執行,與標準流程中的 post-fs-data.sh 時機類似。
此外,通用指令碼可以放置在 /data/adb/late-load.d/ 目錄下,在該階段執行。
模組可以透過 KSU_LATE_LOAD 環境變數偵測當前是否處於 late-load 模式:
if [ "$KSU_LATE_LOAD" = "1" ]; then
# 當前處於 late-load 模式
echo "Late-load mode detected"
fi
這使得模組可以據此調整自身行為,例如跳過僅在早期啟動時才需要的操作。