FUSE 直通式

Android 12 支援 FUSE 直通功能,可盡量減少 FUSE 額外負擔,以便達到與直接存取較低層級檔案系統相近的效能。android12-5.4android12-5.10android-mainline (僅限測試) 核心支援 FUSE 轉送功能,這表示這項功能的支援取決於裝置使用的核心,以及裝置執行的 Android 版本:

  • 從 Android 11 升級至 Android 12 的裝置無法支援 FUSE 轉送,因為這些裝置的核心已凍結,且無法轉移至已正式升級 FUSE 轉送功能的核心。

  • 搭載 Android 12 的裝置可在使用官方核心時支援 FUSE 直通。就這類裝置而言,實作 FUSE 直通式的 Android 架構程式碼會嵌入在 MediaProvider 主系列模組中,而系統會自動升級。未將 MediaProvider 實作為主線程模組的裝置 (例如 Android Go 裝置),也可以存取公開共享的 MediaProvider 變更。

FUSE 與 SDCardFS

使用者空間中的檔案系統 (FUSE) 是一種機制,可將在 FUSE 檔案系統上執行的作業外包給核心 (FUSE 驅動程式) 外包給實作相關作業的使用者空間程式 (FUSE daemon)。Android 11 已淘汰的 SDCardFS,並將 FUSE 做為儲存空間模擬的預設解決方案。在這個變更中,Android 實作了自己的 FUSE 守護程序,用於攔截檔案存取、強制執行額外的安全性和隱私權功能,以及在執行階段操作檔案。

雖然 FUSE 在處理可快取的資訊 (例如頁面或屬性) 時效能良好,但是在存取特別在中階和低階裝置上特別可見的外部儲存空間時,FUSE 的效能就會降低。這些回歸現象是因為 FUSE 檔案系統實作中合作的元件鏈結,以及 FUSE 驅動程式和 FUSE 守護程式之間通訊時,從核心空間切換至使用者空間的多次切換 (相較於直接存取較精簡且在核心中完全實作的較低層級檔案系統)。

為減少這些回歸情形,應用程式可以使用拼接功能減少資料複製作業,並使用 ContentProvider API 直接存取較低層級的檔案系統檔案。即使採用這些和其他最佳化,使用 FUSE 的讀取和寫入作業可能會比直接存取較低層級檔案系統的作業頻寬更低,尤其是在隨機讀取作業中,因為沒有快取或預讀功能可提供協助。若是直接透過舊版 /sdcard/ 路徑存取儲存空間的應用程式,仍會出現明顯的效能下降情形,尤其是在執行大量 IO 作業時。

SDcardFS 使用者空間要求

使用 SDcardFS 可從核心中移除使用者空間呼叫,加快 FUSE 的儲存空間模擬和權限檢查作業。使用者空間要求會依照以下路徑:使用者空間 → VFS → sdcardfs → VFS → ext4 → 頁面快取/儲存空間。

FUSE 傳輸 SDcardFS

圖 1. SDcardFS 使用者空間要求

FUSE 使用者空間要求

FUSE 最初用於啟用儲存空間模擬功能,並允許應用程式以透明方式使用內部儲存空間或外部 SD 卡。使用 FUSE 會產生一些額外負擔,因為每個使用者空間要求都會遵循以下路徑:使用者空間 → VFS → FUSE 驅動程式 → FUSE 守護程序 → VFS → ext4 → 頁面快取/儲存空間。

FUSE 直通 FUSE

圖 2. FUSE 使用者空間要求

FUSE 轉送要求

大多數的檔案存取權限會在檔案開啟時檢查,在讀取及寫入檔案時也會進行額外的權限檢查。在某些情況下,系統可以在檔案開啟時得知要求應用程式具有所要求檔案的完整存取權,因此不需要繼續將讀取和寫入要求從 FUSE 驅動程式轉送至 FUSE 守護程序 (因為這樣只會將資料從一個位置移至另一個位置)。

透過 FUSE 轉送,處理開啟要求的 FUSE 守護程序可通知 FUSE 驅動程式允許該作業,並且所有後續的讀取和寫入要求都能直接轉送至較低層級的檔案系統。這樣一來,您就不會因為等待使用者空間 FUSE 守護程序回覆 FUSE 驅動程式要求而產生額外的額外負擔。

下方比較了 FUSE 和 FUSE 轉送要求。

FUSE 穿透比較

圖 3. FUSE 要求與 FUSE 轉送要求

應用程式執行 FUSE 檔案系統存取作業時,會發生以下作業:

  1. FUSE 驅動程式會處理並排入要求,然後將其呈現給 FUSE 守護程序,後者會透過 /dev/fuse 檔案上的特定連線執行個體處理該 FUSE 檔案系統,而 FUSE 守護程序會阻止讀取該檔案。

  2. 當 FUSE Daemon 收到開啟檔案的要求時,會判定該特定檔案是否應適用 FUSE 直通。如果可用,這個 Daemon 會執行以下操作:

    1. 通知 FUSE 驅動程式此要求。

    2. 使用 FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl 為檔案啟用 FUSE 傳送,此操作必須在已開啟的 /dev/fuse 檔案描述元上執行。

  3. ioctl 會接收 (做為參數) 資料結構,其中包含下列項目:

    • 傳送功能的目標,也就是較低層級檔案系統檔案的檔案描述項。

    • 目前正在處理的 FUSE 要求專屬 ID (必須是開啟或建立並開啟)。

    • 其他可以留空的欄位,適用於日後的實作。

  4. 如果 ioctl 成功,FUSE Daemon 完成開啟的要求,FUSE 驅動程式會處理 FUSE Daemon 回覆,並將較低檔案系統檔案的參照加入核心內的 FUSE 檔案。當應用程式要求在 FUSE 檔案上執行讀取/寫入作業時,FUSE 驅動程式會檢查是否可參照較低層級的檔案系統檔案。

    • 如果有參照,驅動程式會建立新的虛擬檔案系統 (VFS) 要求,並以較低層級的檔案系統檔案做為目標,使用相同的參數。

    • 如果無法取得參照,驅動程式會將要求轉送至 FUSE Daemon。

上述作業會針對一般檔案進行讀取/寫入和讀取/寫入作業,以及對記憶體對應檔案執行讀取/寫入作業。特定檔案的 FUSE 直通功能會持續存在,直到該檔案關閉為止。

實作 FUSE 透視功能

如要在搭載 Android 12 的裝置上啟用 FUSE 轉送功能,請在目標裝置的 $ANDROID_BUILD_TOP/device/…/device.mk 檔案中加入下列行程式碼。

# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
    persist.sys.fuse.passthrough.enable=true

如要停用 FUSE 轉送功能,請略過上述設定變更,或將 persist.sys.fuse.passthrough.enable 設為 false。如果您先前已啟用 FUSE 直通,停用這項功能可防止裝置使用 FUSE 直通,但裝置仍可正常運作。

如要在不刷新裝置的情況下啟用/停用 FUSE 直通式,請使用 ADB 指令變更系統屬性。範例如下所示。

adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot

如需其他協助,請參閱參考實作項目

驗證 FUSE 轉送

如要驗證 MediaProvider 是否使用 FUSE 轉送功能,請檢查 logcat 是否有偵錯訊息。例如:

adb logcat FuseDaemon:V \*:S
--------- beginning of main
03-02 12:09:57.833  3499  3773 I FuseDaemon: Using FUSE passthrough
03-02 12:09:57.833  3499  3773 I FuseDaemon: Starting fuse...

記錄中的 FuseDaemon: Using FUSE passthrough 項目可確保 FUSE 轉送功能正在使用中。

Android 12 CTS 包含 CtsStorageTest,其中包括可觸發 FUSE 直通式的測試。如要手動執行測試,請使用 atest,如下所示:

atest CtsStorageTest