保險絲直通

Android 12 支持 FUSE 直通,將 FUSE 開銷降至最低,從而實現與直接訪問較低文件系統相媲美的性能。 FUSE passthrough 在android12-5.4android12-5.10android-mainline (testing only) 內核中受支持,這意味著對該功能的支持取決於設備使用的內核和設備運行的 Android 版本:

  • 從 Android 11 升級到 Android 12 的設備無法支持 FUSE 直通,因為這些設備的內核已凍結,它們無法遷移到已通過 FUSE 直通更改正式升級的內核。

  • 搭載 Android 12 的設備在使用官方內核時可以支持 FUSE 直通。對於此類設備,實現 FUSE 透傳的 Android 框架代碼嵌入在MediaProvider主線模塊中,該模塊會自動升級。未將 MediaProvider 實現為主線模塊的設備(例如,Android Go 設備)也可以訪問 MediaProvider 更改,因為它們是公開共享的。

FUSE 與 SDCardFS

用戶空間中的文件系統 (FUSE)是一種機制,允許在 FUSE 文件系統上執行的操作由內核(FUSE 驅動程序)外包給執行操作的用戶空間程序(FUSE 守護程序)。 Android 11棄用了 SDCardFS ,並使 FUSE 成為存儲模擬的默認解決方案。作為此更改的一部分,Android 實現了自己的 FUSE 守護程序來攔截文件訪問,實施額外的安全和隱私功能,並在運行時操作文件。

雖然 FUSE 在處理頁面或屬性等可緩存信息時表現良好,但在訪問在中低端設備中尤其明顯的外部存儲時會引入性能倒退。這些回歸是由在 FUSE 文件系統的實現中協作的一系列組件造成的,以及在 FUSE 驅動程序和 FUSE 守護程序之間的通信中從內核空間到用戶空間的多次切換(與直接訪問較低文件相比)更精簡且完全在內核中實現的系統)。

為了減輕這些回歸,應用程序可以使用拼接來減少數據複製並使用ContentProvider API直接訪問較低的文件系統文件。即使進行了這些和其他優化,與直接訪問較低的文件系統相比,使用 FUSE 時讀取和寫入操作的帶寬可能會減少——尤其是隨機讀取操作,其中沒有緩存或預讀可以提供幫助。通過傳統/sdcard/路徑直接訪問存儲的應用程序繼續經歷明顯的性能下降,尤其是在執行 IO 密集型操作時。

SDcardFS 用戶空間請求

使用 SDcardFS 可以通過從內核中移除用戶空間調用來加速 FUSE 的存儲仿真和權限檢查。用戶空間請求遵循以下路徑:用戶空間 → VFS → sdcardfs → VFS → ext4 → 頁面緩存/存儲。

FUSE 直通 SDcardFS

圖 1. SDcardFS 用戶空間請求

FUSE 用戶空間請求

FUSE 最初用於啟用存儲仿真並允許應用程序透明地使用內部存儲或外部 sdcard。使用 FUSE 會帶來一些開銷,因為每個用戶空間請求都遵循以下路徑:用戶空間 → VFS → FUSE 驅動程序 → FUSE 守護進程 → VFS → ext4 → 頁面緩存/存儲。

FUSE 直通保險絲

圖 2. FUSE 用戶空間請求

FUSE 直通請求 {#fuse-passthrough-requests}

大多數文件訪問權限在文件打開時檢查,在讀取和寫入該文件時會進行額外的權限檢查。在某些情況下,可以在文件打開時知道請求應用程序對請求的文件具有完全訪問權限,因此系統不需要繼續將 FUSE 驅動程序的讀取和寫入請求轉發到 FUSE 守護程序(因為只會將數據從一個地方移動到另一個地方)。

使用 FUSE passthrough,處理打開請求的 FUSE 守護進程可以通知 FUSE 驅動程序允許該操作,並且所有後續的讀寫請求都可以直接轉發到較低的文件系統。這避免了等待用戶空間 FUSE 守護程序回复 FUSE 驅動程序請求的額外開銷。

FUSE 和 FUSE 直通請求的比較如下所示。

FUSE 直通比較

圖 3. FUSE 請求與 FUSE 直通請求

當應用程序執行 FUSE 文件系統訪問時,會發生以下操作:

  1. FUSE 驅動程序處理請求並將其排入隊列,然後通過/dev/fuse文件上的特定連接實例將其呈現給處理該 FUSE 文件系統的 FUSE 守護程序,FUSE 守護程序被阻止讀取該文件。

  2. 當 FUSE 守護進程收到打開文件的請求時,它決定 FUSE passthrough 是否應該可用於該特定文件。如果可用,則守護程序:

    1. 通知 FUSE 驅動程序有關此請求。

    2. 使用FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl 為文件啟用 FUSE 直通,這必須在打開的/dev/fuse的文件描述符上執行。

  3. ioctl 接收(作為參數)包含以下內容的數據結構:

    • 作為直通功能目標的較低文件系統文件的文件描述符。

    • 當前正在處理的 FUSE 請求的唯一標識符(必須是 open 或 create-and-open)。

    • 可以留空並用於未來實現的額外字段。

  4. 如果 ioctl 成功,FUSE 守護進程完成打開請求,FUSE 驅動程序處理 FUSE 守護進程回复,並在內核中的 FUSE 文件中添加對較低文件系統文件的引用。當應用程序請求對 FUSE 文件進行讀/寫操作時,FUSE 驅動程序會檢查對較低文件系統文件的引用是否可用。

    • 如果引用可用,驅動程序將創建一個新的虛擬文件系統 (VFS) 請求,該請求具有針對較低文件系統文件的相同參數。

    • 如果引用不可用,驅動程序會將請求轉發給 FUSE 守護程序。

上述操作發生在通用文件上的讀/寫和 read-iter/write-iter 以及內存映射文件上的讀/寫操作。給定文件的 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 passthrough 正在使用中。

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

atest CtsStorageTest