FUSE 패스 스루

Android 12에서는 FUSE 오버헤드를 최소화하여 하위 파일 시스템에 직접 액세스하는 것과 비슷한 성능을 달성하는 FUSE 패스 스루를 지원합니다. FUSE 패스 스루는 android12-5.4, android12-5.10, android-mainline(테스트 전용) 커널에서 지원됩니다. 즉, 이 기능 지원은 기기에서 사용하는 커널과 기기에서 실행되는 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는 처음에 저장소 에뮬레이션을 사용 설정하고 앱이 내부 저장소나 외부 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 드라이버가 요청을 처리하여 큐에 추가하고 /dev/fuse 파일의 특정 연결 인스턴스를 통해 FUSE 파일 시스템을 처리하는 FUSE 데몬에 제공합니다. FUSE 데몬은 이 파일을 읽을 수 없습니다.

  2. FUSE 데몬은 파일 열기 요청을 수신하면 요청된 파일에 FUSE 패스 스루를 사용할 수 있어야 하는지 결정합니다. 사용 가능한 경우 데몬은 다음을 실행합니다.

    1. FUSE 드라이버에 이 요청을 알립니다.

    2. 열린 /dev/fuse의 파일 설명자에서 실행해야 하는 FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl을 사용하여 파일의 FUSE 패스 스루를 사용 설정합니다.

  3. ioctl은 다음이 포함된 데이터 구조를 매개변수로 수신합니다.

    • 패스 스루 기능의 타겟인 하위 파일 시스템 파일의 파일 설명자

    • 현재 처리되고 있는 FUSE 요청의 고유 식별자(열려 있거나 만들고 열어야 함)

    • 비워 둘 수 있는, 향후 구현을 위한 추가 필드

  4. 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.enablefalse로 설정합니다. 이전에 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에는 FUSE 패스 스루를 트리거하는 테스트를 비롯한 CtsStorageTest가 포함되어 있습니다. 테스트를 수동으로 실행하려면 아래와 같이 atest를 사용하세요.

atest CtsStorageTest