Android 12 支援 FUSE 直通,可將 FUSE 負擔降至最低,達到與直接存取較低層級檔案系統相近的效能。android12-5.4、android12-5.10 和 android-mainline (僅供測試) 核心支援 FUSE 傳遞,因此這項功能是否適用於裝置,取決於裝置使用的核心和 Android 版本:
從 Android 11 升級至 Android 12 的裝置無法支援 FUSE 傳遞,因為這些裝置的 Kernel 已凍結,無法移至已正式升級的 Kernel,並進行 FUSE 傳遞變更。
使用官方核心時,搭載 Android 12 的裝置可支援 FUSE 直通。對於這類裝置,實作 FUSE 直通的 Android 架構程式碼會嵌入 MediaProvider 主系列模組,並自動升級。未將 MediaProvider 實作為主線模組的裝置 (例如 Android Go 裝置),也能存取公開分享的 MediaProvider 變更。
FUSE 與 SDCardFS
使用者空間中的檔案系統 (FUSE) 是一種機制,可讓核心 (FUSE 驅動程式) 將對 FUSE 檔案系統執行的作業外包給使用者空間程式 (FUSE 精靈),該程式會實作這些作業。Android 11 已淘汰 SDCardFS,並將 FUSE 設為儲存空間模擬的預設解決方案。為配合這項變更,Android 實作了專屬的 FUSE Daemon,可攔截檔案存取作業、強制執行額外的安全性和隱私權功能,以及在執行階段操控檔案。
FUSE 在處理可快取的資訊 (例如網頁或屬性) 時表現良好,但在存取外部儲存空間時會導致效能回歸,尤其是在中低階裝置上更是明顯。這些回歸問題是由一連串相互合作的元件所造成,這些元件實作了 FUSE 檔案系統,而且 FUSE 驅動程式和 FUSE 精靈之間的通訊會多次從核心空間切換至使用者空間 (相較於直接存取較精簡的下層檔案系統,後者完全在核心中實作)。
為減輕這些回歸問題,應用程式可使用拼接來減少資料複製作業,並使用 ContentProvider API 直接存取較低的檔案系統檔案。即使採用這些其他最佳化措施,與直接存取較低層級的檔案系統相比,使用 FUSE 時的讀取和寫入作業頻寬仍可能減少,尤其是隨機讀取作業,因為這類作業無法使用快取或預先讀取。如果應用程式透過舊版 /sdcard/ 路徑直接存取儲存空間,效能會明顯下降,尤其是在執行大量 I/O 作業時。
SDcardFS 使用者空間要求
使用 SDcardFS 可從核心移除使用者空間呼叫,加快 FUSE 的儲存空間模擬和權限檢查速度。使用者空間要求遵循以下路徑:使用者空間 → VFS → sdcardfs → VFS → ext4 → 頁面快取/儲存空間。
圖 1. SDcardFS 使用者空間要求
FUSE 使用者空間要求
FUSE 最初是用來啟用儲存空間模擬功能,並允許應用程式以透明方式使用內部儲存空間或外部 SD 卡。使用 FUSE 會產生一些額外負擔,因為每個使用者空間要求都會經過以下路徑:使用者空間 → VFS → FUSE 驅動程式 → FUSE 精靈 → VFS → ext4 → 頁面快取/儲存空間。
圖 2. FUSE 使用者空間要求
FUSE 傳遞要求
系統會在開啟檔案時檢查大多數的檔案存取權限,並在讀取及寫入該檔案時進行額外的權限檢查。在某些情況下,系統可以在開啟檔案時判斷要求應用程式是否具有所要求檔案的完整存取權,因此不需要繼續將 FUSE 驅動程式的讀取和寫入要求轉送至 FUSE 精靈 (因為這樣只會將資料從一個位置移至另一個位置)。
透過 FUSE 直通,處理開啟要求的 FUSE 精靈可以通知 FUSE 驅動程式作業已獲准,且所有後續的讀取和寫入要求都可以直接轉送至較低的檔案系統。這樣可避免額外負擔,不必等待使用者空間 FUSE 精靈回覆 FUSE 驅動程式要求。
以下比較 FUSE 和 FUSE 傳遞要求。
圖 3. FUSE 要求與 FUSE 直通要求
應用程式執行 FUSE 檔案系統存取作業時,會發生下列情況:
FUSE 驅動程式會處理要求並將其加入佇列,然後透過
/dev/fuse檔案上的特定連線執行個體,將要求提供給處理該 FUSE 檔案系統的 FUSE Daemon,而 FUSE Daemon 無法讀取該檔案。當 FUSE 精靈收到開啟檔案的要求時,會決定該檔案是否應使用 FUSE 直通。如果可用,精靈會執行下列操作:
將這項要求通知 FUSE 驅動程式。
使用
FUSE_DEV_IOC_PASSTHROUGH_OPENioctl 為檔案啟用 FUSE 直通,這必須在已開啟/dev/fuse的檔案描述元上執行。
ioctl 會接收 (做為參數) 包含下列項目的資料結構:
下層檔案系統檔案的檔案描述元,是直通功能的目標。
目前正在處理的 FUSE 要求專屬 ID (必須為開啟或建立並開啟)。
可留空的額外欄位,適用於日後實作。
如果 ioctl 成功,FUSE 精靈會完成開啟要求,FUSE 驅動程式會處理 FUSE 精靈回覆,且核心內的 FUSE 檔案會新增對較低檔案系統檔案的參照。應用程式要求對 FUSE 檔案執行讀取/寫入作業時,FUSE 驅動程式會檢查是否可參照較低層級的檔案系統檔案。
如果參照可用,驅動程式會建立新的虛擬檔案系統 (VFS) 要求,並以相同參數為目標,指向較低的檔案系統檔案。
如果沒有參照,驅動程式會將要求轉送至 FUSE 常駐程式。
上述作業會針對一般檔案的讀取/寫入和讀取疊代/寫入疊代,以及記憶體對應檔案的讀取/寫入作業執行。特定檔案的 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 rootadb 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