عبور فیوز

اندروید ۱۲ از گذرگاه FUSE پشتیبانی می‌کند که سربار FUSE را به حداقل می‌رساند تا عملکردی قابل مقایسه با دسترسی مستقیم به سیستم فایل پایین‌تر داشته باشد. گذرگاه FUSE در کرنل‌های android12-5.4 ، android12-5.10 و android-mainline (فقط برای آزمایش) پشتیبانی می‌شود، به این معنی که پشتیبانی از این ویژگی به کرنل مورد استفاده دستگاه و نسخه اندروید در حال اجرا بستگی دارد:

  • دستگاه‌هایی که از اندروید ۱۱ به اندروید ۱۲ ارتقا می‌یابند، نمی‌توانند از گذرگاه FUSE پشتیبانی کنند، زیرا هسته‌های این دستگاه‌ها فریز شده‌اند و نمی‌توانند به هسته‌هایی که رسماً با تغییرات گذرگاه FUSE ارتقا یافته‌اند، منتقل شوند.

  • دستگاه‌هایی که با اندروید ۱۲ عرضه می‌شوند، می‌توانند هنگام استفاده از یک هسته رسمی، از گذرگاه FUSE پشتیبانی کنند. برای چنین دستگاه‌هایی، کد چارچوب اندروید که گذرگاه FUSE را پیاده‌سازی می‌کند، در ماژول اصلی MediaProvider تعبیه شده است که به طور خودکار ارتقا می‌یابد. دستگاه‌هایی که MediaProvider را به عنوان یک ماژول اصلی پیاده‌سازی نمی‌کنند (به عنوان مثال، دستگاه‌های Android Go)، همچنین می‌توانند به تغییرات MediaProvider دسترسی داشته باشند، زیرا آنها به صورت عمومی به اشتراک گذاشته می‌شوند.

فیوز در مقابل SDCardFS

سیستم فایل در فضای کاربری (FUSE) مکانیزمی است که اجازه می‌دهد عملیات انجام شده روی یک سیستم فایل FUSE توسط هسته (درایور FUSE) به یک برنامه فضای کاربری (FUSE daemon) برون‌سپاری شود، که این عملیات را پیاده‌سازی می‌کند. اندروید ۱۱ ، SDCardFS را منسوخ کرد و FUSE را به عنوان راه‌حل پیش‌فرض برای شبیه‌سازی ذخیره‌سازی قرار داد. به عنوان بخشی از این تغییر، اندروید FUSE daemon مخصوص خود را برای رهگیری دسترسی به فایل‌ها، اعمال ویژگی‌های امنیتی و حریم خصوصی اضافی و دستکاری فایل‌ها در زمان اجرا پیاده‌سازی کرد.

اگرچه FUSE هنگام کار با اطلاعات قابل ذخیره‌سازی مانند صفحات یا ویژگی‌ها عملکرد خوبی دارد، اما هنگام دسترسی به حافظه خارجی، رگرسیون‌های عملکردی را ایجاد می‌کند که به ویژه در دستگاه‌های میان‌رده و پایین‌رده قابل مشاهده است. این رگرسیون‌ها توسط زنجیره‌ای از اجزا که در پیاده‌سازی سیستم فایل FUSE همکاری می‌کنند، و همچنین سوئیچ‌های متعدد از فضای هسته به فضای کاربر در ارتباطات بین درایور FUSE و دیمن FUSE ایجاد می‌شوند (در مقایسه با دسترسی مستقیم به سیستم فایل پایین‌تر که سبک‌تر است و به طور کامل در هسته پیاده‌سازی شده است).

برای کاهش این رگرسیون‌ها، برنامه‌ها می‌توانند از splicing برای کاهش کپی کردن داده‌ها استفاده کنند و از ContentProvider API برای دسترسی مستقیم به فایل‌های سیستم فایل پایین‌تر استفاده کنند. حتی با وجود این بهینه‌سازی‌ها و سایر بهینه‌سازی‌ها ، عملیات خواندن و نوشتن ممکن است هنگام استفاده از FUSE در مقایسه با دسترسی مستقیم به سیستم فایل پایین‌تر، پهنای باند کمتری را تجربه کنند - به خصوص با عملیات خواندن تصادفی، که در آن هیچ ذخیره‌سازی یا خواندن از قبل نمی‌تواند کمکی کند. و برنامه‌هایی که مستقیماً از طریق مسیر قدیمی /sdcard/ به فضای ذخیره‌سازی دسترسی دارند، همچنان افت عملکرد قابل توجهی را تجربه می‌کنند، به خصوص هنگام انجام عملیات فشرده IO.

درخواست‌های فضای کاربری SDcardFS

استفاده از SDcardFS می‌تواند با حذف فراخوانی فضای کاربر از هسته، شبیه‌سازی فضای ذخیره‌سازی و بررسی مجوزهای FUSE را سرعت بخشد. درخواست‌های فضای کاربری از مسیر زیر پیروی می‌کنند: Userspace → VFS → sdcardfs → VFS → ext4 → Page cache/Storage.

فیوز عبوری SDcardFS

شکل ۱. درخواست‌های فضای کاربری SDcardFS

درخواست‌های فضای کاربری FUSE

FUSE در ابتدا برای فعال کردن شبیه‌سازی فضای ذخیره‌سازی و اجازه دادن به برنامه‌ها برای استفاده شفاف از حافظه داخلی یا کارت حافظه خارجی استفاده می‌شد. استفاده از FUSE مقداری سربار ایجاد می‌کند زیرا هر درخواست فضای کاربری از مسیر زیر پیروی می‌کند: Userspace → VFS → FUSE driver → FUSE daemon → VFS → ext4 → Page cache/Storage.

فیوز عبوری

شکل ۲. درخواست‌های فضای کاربری FUSE

درخواست‌های عبور فیوز

بیشتر مجوزهای دسترسی به فایل در زمان باز شدن فایل بررسی می‌شوند، و بررسی‌های مجوز اضافی هنگام خواندن و نوشتن در آن فایل انجام می‌شود. در برخی موارد، می‌توان در زمان باز شدن فایل فهمید که برنامه درخواست‌کننده دسترسی کامل به فایل درخواستی دارد، بنابراین سیستم نیازی به ادامه ارسال درخواست‌های خواندن و نوشتن از درایور FUSE به دیمن FUSE ندارد (زیرا این کار فقط داده‌ها را از یک مکان به مکان دیگر منتقل می‌کند).

با استفاده از گذرگاه FUSE، دیمن FUSE که یک درخواست باز را مدیریت می‌کند، می‌تواند به درایور FUSE اطلاع دهد که عملیات مجاز است و تمام درخواست‌های خواندن و نوشتن بعدی می‌توانند مستقیماً به سیستم فایل پایین‌تر ارسال شوند. این امر از سربار اضافی انتظار برای دیمن FUSE فضای کاربر برای پاسخ به درخواست‌های درایور FUSE جلوگیری می‌کند.

مقایسه‌ای از درخواست‌های عبوری FUSE و FUSE در زیر نشان داده شده است.

مقایسه عبور فیوز

شکل ۳. درخواست FUSE در مقابل درخواست عبور FUSE

وقتی یک برنامه به سیستم فایل FUSE دسترسی پیدا می‌کند، عملیات زیر رخ می‌دهد:

  1. درایور FUSE درخواست را مدیریت و در صف انتظار قرار می‌دهد، سپس آن را به دیمن FUSE ارائه می‌دهد که سیستم فایل FUSE را از طریق یک نمونه اتصال خاص روی فایل /dev/fuse مدیریت می‌کند، که دیمن FUSE از خواندن آن فایل مسدود شده است.

  2. وقتی سرویس FUSE درخواستی برای باز کردن یک فایل دریافت می‌کند، تصمیم می‌گیرد که آیا گذرگاه FUSE باید برای آن فایل خاص در دسترس باشد یا خیر. اگر در دسترس باشد، سرویس:

    1. درایور FUSE را در مورد این درخواست مطلع می‌کند.

    2. با استفاده از ioctl مربوط به FUSE_DEV_IOC_PASSTHROUGH_OPEN ، که باید روی توصیف‌گر فایلِ باز شده‌ی /dev/fuse انجام شود، عبور FUSE را برای فایل فعال می‌کند.

  3. تابع ioctl (به عنوان پارامتر) یک ساختار داده دریافت می‌کند که شامل موارد زیر است:

    • توصیف‌گر فایل مربوط به فایل سیستم فایل پایینی که هدف ویژگی عبور از میان فایل‌ها است.

    • شناسه منحصر به فرد درخواست FUSE که در حال حاضر در حال پردازش است (باید باز یا ایجاد و باز باشد).

    • فیلدهای اضافی که می‌توانند خالی بمانند و برای پیاده‌سازی‌های آینده در نظر گرفته شده‌اند.

  4. اگر ioctl با موفقیت اجرا شود، سرویس FUSE درخواست باز را تکمیل می‌کند، درایور FUSE پاسخ سرویس FUSE را مدیریت می‌کند و ارجاعی به فایل سیستم فایل پایین‌تر به فایل FUSE در هسته اضافه می‌شود. وقتی یک برنامه درخواست عملیات خواندن/نوشتن روی یک فایل FUSE را می‌دهد، درایور FUSE بررسی می‌کند که آیا ارجاع به یک فایل سیستم فایل پایین‌تر در دسترس است یا خیر.

    • اگر مرجعی در دسترس باشد، درایور یک درخواست سیستم فایل مجازی (VFS) جدید با همان پارامترها ایجاد می‌کند که فایل سیستم فایل پایین‌تر را هدف قرار می‌دهد.

    • اگر مرجعی در دسترس نباشد، درایور درخواست را به سرویس FUSE ارسال می‌کند.

عملیات فوق برای خواندن/نوشتن و خواندن-تکرار/نوشتن-تکرار روی فایل‌های عمومی و عملیات خواندن/نوشتن روی فایل‌های نگاشت شده در حافظه انجام می‌شود. گذرگاه FUSE برای یک فایل معین تا زمانی که آن فایل بسته شود، وجود دارد.

پیاده‌سازی گذرگاه فیوز

برای فعال کردن FUSE passthrough در دستگاه‌هایی که اندروید ۱۲ را اجرا می‌کنند، خطوط زیر را به فایل $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 passthrough بدون فلش کردن دستگاه، ویژگی سیستم را با استفاده از دستورات ADB تغییر دهید. مثالی در زیر نشان داده شده است.

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

برای کمک بیشتر، به پیاده‌سازی مرجع مراجعه کنید.

اعتبارسنجی گذرگاه فیوز

برای تأیید اینکه 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 در حال استفاده است.

CTS اندروید ۱۲ شامل CtsStorageTest است که شامل تست‌هایی است که FUSE passthrough را فعال می‌کنند. برای اجرای دستی تست، از atest مطابق شکل زیر استفاده کنید:

atest CtsStorageTest