---
url: https://kernelsu.org/zh_TW/guide/module.md
---
# 模組指南 {#module-guide}

KernelSU 提供了一個模組機制，它可以在保持系統分割區完整性的同時達到修改系統分割區的效果；這種機制一般被稱為 systemless (無系統修改)。

KernelSU 的模組運作機制與 Magisk 幾乎相同，如果您熟悉 Magisk 模組的開發，那麼開發 KernelSU 的模組大同小異，您可以跳過下列有關模組的介紹，只需要瞭解 [KernelSU 模組與 Magisk 模組的差異](difference-with-magisk.md)。

::: warning METAMODULE 僅在修改系統檔案時需要
KernelSU 使用 [metamodule](metamodule.md) 架構來掛載 `system` 目錄。**僅當您的模組需要修改 `/system` 檔案時**(透過 `system` 目錄)，您才需要安裝 metamodule(如 [meta-overlayfs](https://github.com/tiann/KernelSU/releases))。其他模組功能如腳本、sepolicy 規則和 system.prop 無需 metamodule 即可運作。
:::

## WebUI

KernelSU 的模組支援顯示互動介面，請參閱 [WebUI 文檔](module-webui.md).

## 模組配置

KernelSU 提供了一個內建的配置系統，允許模組儲存持久化或暫時的鍵值設定。詳情請參閱[模組配置文檔](module-config.md)。

## 一次性腳本

模組 ZIP 也可以是一次性腳本：KernelSU 管理器只執行它一次，而不是安裝它。詳情請參閱[一次性腳本文檔](module-script.md)。

## Busybox

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 之外使用這個「獨立模式」功能的使用者，有兩種啟用方法：

1. 將環境變數 `ASH_STANDALONE` 設為 `1`。例如：`ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>`
2. 使用命令列選項切換：`/data/adb/ksu/bin/busybox sh -o standalone <script>`

為了確保所有後續的 `sh` shell 都在獨立模式下執行，第一種是首選方法 (這也是 KernelSU 和 KernelSU 管理器內部使用的方法)，因為環境變數會被繼承到子執行緒中。

::: tip 與 Magisk 的差異
KernelSU 的 BusyBox 現在是直接使用 Magisk 專案編譯的二進位檔案，**感謝 Magisk！**
因此，您完全不必擔心 BusyBox 腳本與在 Magisk 和 KernelSU 之間的相容性問題，因為它們完全相同！
:::

## KernelSU 模組 {#kernelsu-modules}

KernelSU 模組是一個放置於 `/data/adb/modules` 且滿足下列結構的資料夾：

```txt
/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 原則將會在系統開機時載入
│   ├── initrc/             <--- 此目錄下的 .rc 檔案將在啟動時注入 init.rc
│   │   ├── myservice.rc
│   │   └── ...
│   │
│   │      *** 自動產生的目錄，不要手動建立或修改！ ***
│   │
│   ├── 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](https://github.com/Dr-TSNG/ZygiskNext) 以支援 Zygisk 模組，此時 Zygisk 模組的內容與 Magisk 所支援的 Zygisk 完全相同。
:::

### module.prop

module.prop 是一個模組的設定檔，在 KernelSU 中如果模組中不包含這個檔案，那麼它將不被認為是一個模組；這個檔案的格式如下：

```txt
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
updateJson=<url> (optional)
actionIcon=<path> (optional)
webuiIcon=<path> (optional)
```

* id 必須與這個正則表達式相符：`^[a-zA-Z][a-zA-Z0-9._-]+$` 例如：✓ `a_module`，✓ `a.module`，✓ `module-101`，✗ `a module`，✗ `1_module`，✗ `-a-module`。這是您的模組的唯一識別碼，發表後將無法變更。
* versionCode 必須是一個整數，用於比較版本。
* 其他未在上方提到的內容可以是任何單行字串。
* 請確保使用 `UNIX (LF)` 分行符號類型，而非 `Windows (CR + LF)` 或 `Macintosh (CR)`。
* actionIcon 與 webuiIcon 是可選的圖示路徑，會作為管理器中模組
  Action 捷徑與 WebUI 捷徑的預設圖示。這些路徑必須是以模組根目錄為
  基準的相對路徑。例如：`actionIcon=icon/icon.png`
  會解析為 `<MODDIR>/icon/icon.png`。

::: tip 動態描述
`description` 欄位可以在執行階段使用模組配置系統動態覆蓋。詳情請參閱[覆蓋模組描述](module-config.md#overriding-module-description)。
:::

### Shell 腳本 {#shell-scripts}

請閱讀 [開機腳本](#boot-scripts) 章節，以瞭解 `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` 分區之上，這表示：

1. 系統中對應目錄的相同名稱的檔案會被此目錄中的檔案覆寫。
2. 系統中對應目錄的相同名稱的檔案會與此目錄的檔案合併。

如果您想要刪除系統先前的目錄中的某個檔案或資料夾，您需要在模組目錄中透過 `mknod filename c 0 0` 以建立一個 `filename` 的相同名稱的檔案；這樣 overlayfs 系統會自動「whiteout」等效刪除這個檔案 (`/system` 分割區並未被變更)。

您也可以在 `customize.sh` 中宣告一個名為 `REMOVE` 並且包含一系列目錄的變數以執行移除作業，KernelSU 會自動為您在模組對應目錄執行 `mknod <TARGET> c 0 0`。例如：

```sh
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 會自動為您在模組對應目錄執行相關作業。例如：

```sh
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 的文檔](https://docs.kernel.org/filesystems/overlayfs.html)

### system.prop

這個檔案的格式與 `build.prop` 完全相同：每一行都是由 `[key]=[value]` 組成。

### sepolicy.rule

如果您的模組需要一些額外 sepolicy 修補，請將這些原則新增至這個檔案中。這個檔案的每一行都將被視為一個原則陳述。

### initrc 注入 {#initrc-injection}

KernelSU 提供了一種將自訂 Android Init RC 指令注入系統 `init.rc` 的機制。這使得模組可以在不修改系統分區的情況下註冊自訂的 Android 服務、設定屬性觸發器或執行其他 Init 語言操作。

在啟動過程中，KernelSU 的核心模組透過 hook `read()` 和 `fstat()` 系統調用，在 Android init 進程讀取 `/system/etc/init/hw/init.rc` 時，將自訂 RC 內容透明地附加到文件末尾。init 進程會像處理原始 init.rc 內容一樣解析這些注入的指令。

在使用者空間側，ksud 會將所有已啟用模組的 `.rc` 檔案合併到一個 `modules.rc` 檔案中，存放在 `/metadata` 分區上。每當模組狀態發生變化（安裝、啟用、停用、解除安裝等）時，這個檔案都會被自動重新產生。

#### 模組 initrc 檔案

在模組目錄中建立一個 `initrc/` 子目錄，將 `.rc` 檔案放置其中：

```txt
/data/adb/modules/<MODID>/
├── initrc/
│   ├── myservice.rc
│   └── another.rc
└── ...
```

::: tip

* 檔案必須以 `.rc` 為副檔名。
* 只要模組處於啟用狀態，`initrc/` 目錄下的所有 `.rc` 檔案都會被包含（無需設定可執行權限）。
* 處理的先後順序為：同一目錄內的檔案按**檔案名稱字母順序**排列；模組之間按**模組 ID 字母順序**排列。
  :::

#### 通用 initrc 檔案

除了模組層級的 RC 檔案外，您還可以將 `.rc` 檔案放置在全域目錄中：

```txt
/data/adb/initrc.d/
├── myservice.rc
└── another.rc
```

::: warning 全域 initrc 檔案需要可執行權限
與模組 `initrc/` 目錄不同，`/data/adb/initrc.d/` 中的檔案**必須設定可執行權限**才會被包含。不具有可執行權限的 `.rc` 檔案會被靜默跳過。
:::

全域 `initrc.d/` 檔案在所有模組 RC 檔案之前被處理。

#### 範例

以下是一個註冊自訂 Android 服務的 `.rc` 檔案範例：

```rc
service myservice /data/adb/modules/mymodule/bin/myservice
    user root
    group root
    disabled
    seclabel u:r:ksu:s0

on property:sys.boot_completed=1
    start myservice
```

此檔案放置在 `/data/adb/modules/mymodule/initrc/myservice.rc`，將在系統啟動時註冊一個名為 `myservice` 的服務，並在 `sys.boot_completed=1` 時啟動它。

#### 手動重新整理

您可以透過以下指令手動觸發 `modules.rc` 重新產生（變更將在下次啟動時生效）：

```sh
ksud initrc refresh
```

::: tip

* initrc 注入發生在啟動極早期（init 讀取 init.rc 時），**早於** post-fs-data 和所有模組腳本的執行。
* 注入的 RC 內容被 init 當作原始 init.rc 的一部分處理，支援所有 Android Init 語言語法（服務定義、觸發器、屬性設定等）。
* 在 **late-load 模式**下，initrc 注入**不可用**，因為系統調用 hook 在該模式下不會被安裝。
* 可以在 ksud 修補映像檔的時候傳入參數 `--no-custom-rc` 停用模組 RC 注入。
  :::

## 模組安裝程式 {#module-installer}

KernelSU 的模組安裝程式就是一個可以透過 KernelSU 管理員應用程式刷新的 Zip 檔案，這個 Zip 檔案的格式如下：

```txt
module.zip
│
├── customize.sh                       <--- (Optional, more details later)
│                                           This script will be sourced by update-binary
├── ...
├── ...  /* 其他模块文件 */
│
```

:::warning 警告
KernelSU 模組不支援在 Recovery 中安裝！！
:::

### 自訂安裝過程 {#customizing-installation}

如果您想要控制模組的安裝過程，可以在模組的目錄下建立一個名為 `customize.sh` 的檔案，這個檔案將會在模組被解壓縮後經由 **source** 調用，如果您的模組需要依據裝置的 API 版本或裝置架構執行一些額外的作業，這個腳本將非常有用。

如果您想完全控制腳本的安裝過程，您可以在 `customize.sh` 中宣告 `SKIPUNZIP=1` 以跳過所有的預設安裝步驟。此時，您需要自行處理所有的安裝過程(例如解壓縮模組、設定權限等)。

`customize.sh` 腳本以「獨立模式」執行在 KernelSU 的 BusyBox `ash` shell 中。您可以使用下列變數和函式：

#### 變數 {#variables}

* `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 中永遠為 `true`
* `MODPATH` (path): 目前模組的安裝目錄
* `TMPDIR` (path): 可以存放暫存檔的位置
* `ZIPFILE` (path): 目前模組的安裝程式 Zip
* `ARCH` (string): 裝置的 CPU 架構，有這幾種：`arm`, `arm64`, `x86`, or `x64`
* `IS64BIT` (bool): 是否為 64 位元裝置
* `API` (int): 目前裝置的 Android API 版本 (例如 Android 6.0 上為 `23`)
* `KSU_UAPI_VER` (int): KernelSU 使用者空間 (ksud) 的 UAPI 版本號 (例如 `2`)。當核心驅動發生破壞性更改時此版本號會遞增，模組可據此判斷相容性。
* `KSU_RUNTIME_MODE` (string): KernelSU 目前的執行模式。可能的值為 `built-in`（即 GKI 模式，編譯進核心）、`lkm`（開機時作為核心模組載入）或 `late-load`（開機後作為核心模組載入）。
* `KSU_LATE_LOAD` (int?): 如果 KernelSU 是在開機後延遲載入的，此變數的值為 `1`，否則不設定此變數。

::: warning 警告
`MAGISK_VER_CODE` 在 KernelSU 永遠為 `25200`，`MAGISK_VER` 則為 `v25.2`，請不要透過這兩個變數來判斷是否為 KernelSU！
:::

#### 函式 {#functions}

```txt
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
```

## 開機腳本 {#boot-scripts}

在 KernelSU 中，依據腳本執行模式的不同分為兩種：post-fs-data 模式和 late_start 服務模式。

* post-fs-data 模式
  * 這個階段是 **阻塞** 的。在執行完成之前或 10 秒鐘之後，開機程序會被暫停。
  * 腳本在任何模組被掛接之前執行。這使模組開發人員可以在模組被掛接之前動態調整他們的模組。
  * 這個階段發生在 Zygote 啟動之前，這意味著 Android 中的一切。
  * **警告**: 使用 `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 中執行，並啟用**獨立模式**。

### 開機腳本解釋 {#boot-scripts-process-explanation}

以下是Android的相關啟動流程（部分省略），其中包括KernelSU的運行（帶前導星號），可以幫助您更好地理解這些模組腳本的用途：

```txt
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
  *initrc injection: Kernel hook appends KernelSU core RC and module modules.rc to 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 語言感興趣，建議閱讀它的 [文檔](https://android.googlesource.com/platform/system/core/+/master/init/README.md).

## Late-load 模式 {#late-load-mode}

除了上述標準啟動流程外，KernelSU 還支援 **late-load 模式**，用於 LKM（可載入核心模組）場景。在該模式下，KernelSU 核心模組在**系統完全啟動後**載入，而非在 init 過程中載入。

### 什麼時候觸發 late-load？

透過執行 `ksud late-load` 命令觸發。該命令會：

1. 偵測當前 KMI 版本，從內嵌資源中載入對應的 `kernelsu.ko`。
2. 執行模組初始化（SELinux 規則、白名單、feature 等），這些工作在標準啟動中發生在 boot 階段。

由於系統已經完全運行，某些啟動時的機制不可用或不需要。

### 與標準啟動的差異

| 行為 | 標準啟動 | Late-load 模式 |
|------|:---:|:---:|
| 核心模組由 init (PID 1) 載入 | 是 | 否（啟動後載入） |
| initrc 注入（模組 `.rc` 檔案注入 init.rc） | 是 | 不可用 |
| 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 模式下，指令碼執行順序如下：

```txt
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 專用指令碼

模組可以提供 `late-load.sh` 指令碼，該指令碼**僅在 late-load 模式下執行**，作為 `post-fs-data.sh` 的替代。該指令碼在 OverlayFS 掛載之前執行，與標準流程中的 `post-fs-data.sh` 時機類似。

此外，通用指令碼可以放置在 `/data/adb/late-load.d/` 目錄下，在該階段執行。

### 在指令碼中偵測 late-load 模式

模組可以透過 `KSU_LATE_LOAD` 環境變數偵測當前是否處於 late-load 模式：

```sh
if [ "$KSU_LATE_LOAD" = "1" ]; then
    # 當前處於 late-load 模式
    echo "Late-load mode detected"
fi
```

這使得模組可以據此調整自身行為，例如跳過僅在早期啟動時才需要的操作。
