Poglobitev v SDCardFS: Kako bo Googlova zamenjava FUSE zmanjšala V/I stroške

Poglobljeno raziskovanje SDCardFS, Googlove zamenjave za FUSE, in kako bo njena implementacija zmanjšala V/I stroške.

Pred nekaj meseci je Google dodal nekaj, kar se imenuje "SDCardFS” v uradne veje AOSP za jedro Linuxa. Takrat je potezo opazil le nekateri razvijalci jedra, sicer pa je uletela pod radar večine uporabnikov. To ni presenetljivo glede na dejstvo, da večina uporabnikov, vključno z mano, ne ve, kaj se dogaja pod pokrovom operacijskega sistema Android in njegovega jedra.

Vendar pa najnovejša epizoda oddaje Razvijalci za Android v zakulisju podcast obnovil zanimanje za to temo. Podcast, ki ga je gostil Chet Haase (višji programski inženir pri Googlu), je raziskal nedavne in prihajajoče spremembe jedra. V oddaji je bil Rom Lemarchand, razvijalec jedra Linuxa, ki dela v skupini za Android. Dvojec je predvsem razpravljal o spremembah, ki so bile narejene za prilagoditev posodobitev A/B, vendar je v zadnjih 5 minutah epizode gospod Lemarchand govoril o "naslednji veliki stvari", na kateri dela njegova ekipa - SDCardFS.

Moram priznati, da sem za obstoj SDCardFS izvedel po poslušanju tega podcasta. Seveda pa nisem bil edini, ki se je zanimal za to temo, saj nedavna nit Reddit je pokazal. Vendar pa nisem bil zadovoljen z osnovno razlago, ki je bila ponujena v podcastu, in v prizadevanju, da bi razblinil nekaj napačnih informacij, ki se širijo naokoli, sem opravil nekaj lastnih raziskav in se pogovoril z nekaj strokovnjaki z ustreznim znanjem o zadeva.

Najlepša hvala razvijalcu programske opreme Michalu Kowalczyku, ker je prispeval svoje znanje k temu članku in ker si je vzel čas za odgovore na moja vprašanja.


"Zunanje" je v resnici notranje

Takoj na začetku bo gotovo nekaj napačnih predstav, ki jih moramo razjasniti - drugače bo preostanek članka zelo zmeden. Koristno je razpravljati o zgodovini kartic SD in telefonov Android.

V zgodnjih dneh telefonov Android se je skoraj vsaka naprava zanašala na uporabo svojih kartic microSD za shranjevanje. To je bilo posledica dejstva, da so bili telefoni v tistem času dobavljeni z majhnimi notranjimi zmogljivostmi za shranjevanje. Vendar kartice SD, ki se uporabljajo za shranjevanje aplikacij, pogosto ne zagotavljajo zvezdniške uporabniške izkušnje, vsaj v primerjavi s hitrostjo, s katero lahko notranji bliskovni pomnilnik bere/piše podatke. Zato je vedno večja uporaba kartic SD za zunanje shranjevanje podatkov postala skrb za Googlovo uporabniško izkušnjo.

Zaradi zgodnjega širjenja kartic SD kot zunanjih pomnilniških naprav so konvencije o poimenovanju pomnilnika Android temeljile na dejstvu, da ima vsaka naprava dejansko fizično režo za kartico microSD. Toda tudi na napravah, ki niso vsebovale reže za kartico SD, je bila oznaka /sdcard še vedno uporabljena za kazanje na dejanski notranji čip za shranjevanje. Bolj zmedeno je dejstvo, da bi naprave, ki so za shranjevanje uporabljale tako fizično kartico SD kot tudi čip za shranjevanje z visoko zmogljivostjo, pogosto poimenovale svoje particije na podlagi kartice SD. Na primer, v teh napravah bi se točka vpetja /sdcard nanašala na dejanski notranji čip za shranjevanje, medtem ko bi se nekaj podobnega /storage/sdcard1 nanašalo na fizično zunanjo kartico.

Torej, čeprav se kartica microSD praktično šteje za zunanji pomnilnik, je konvencija o poimenovanju povzročila, da je »SDCard« ostal dolgo po kakršni koli dejanski uporabi fizične kartice. Ta zmeda s shranjevanjem je povzročala tudi nekaj glavobola razvijalcem aplikacij zaradi dejstva, da so bili podatki aplikacije in njeni nosilci ločeni med obe particiji.

Zaradi premajhnega prostora za shranjevanje prvih čipov notranjega pomnilnika so uporabniki razočarano ugotovili, da ne morejo več nameščati aplikacij (ker je bila particija /data polna). Medtem so bile njihove kartice microSD z večjo zmogljivostjo preusmerjene na shranjevanje samo medijev (kot so fotografije, glasba in filmi). Uporabniki, ki so nekoč brskali po naših forumih, se morda spomnijo teh imen: Link2SD in Apps2SD. To so bile (korenske) rešitve, ki so uporabnikom omogočale namestitev svojih aplikacij in njihovih podatkov na fizično kartico SD. A te rešitve še zdaleč niso bile popolne, zato je moral posredovati Google.

Znano je, da je Google zelo zgodaj ukinil kartice SD. Nexus One ostaja edina naprava Nexus z režo za kartico microSD (in tako bo za vedno, saj je blagovna znamka Nexus dejansko mrtva). Z Nexusom S je zdaj obstajala le ena, poenotena particija za shranjevanje vseh aplikacijskih podatkov in medijev - particija /data. Kar je bilo nekoč znano kot točka priklopa /sdcard, se je zdaj preprosto nanašalo na navidezni datotečni sistem (implementiran pod VAROVALKA protokol, kot je opisano spodaj), ki se nahaja v podatkovni particiji - /data/media/0.

Da bi ohranil združljivost in zmanjšal zmedo, je Google še vedno uporabljal to zdaj virtualno particijo »sdcard« za shranjevanje predstavnosti. Toda zdaj, ko je bila ta navidezna particija »sdcard« dejansko v /data, bi vse, kar je shranjeno v njej, štelo v prostor za shranjevanje notranjega pomnilniškega čipa. Tako so morali proizvajalci originalne opreme razmisliti, koliko prostora bodo dodelili aplikacijam (/data) v primerjavi z mediji (/data/media).

Dve zelo različni "SD kartici"

Google je upal, da bodo proizvajalci sledili njihovemu zgledu in se znebili kartic SD. K sreči je sčasoma proizvajalcem telefonov uspelo pridobiti te komponente z večjo zmogljivostjo, hkrati pa so ostali stroškovno učinkoviti, zato je potreba po karticah SD začela zmanjkovati. Toda konvencije o poimenovanju so se ohranile, da bi zmanjšale količino truda, ki bi ga morali razvijalci in proizvajalci originalne opreme vložiti v prilagoditev. Trenutno, ko govorimo o "zunanjem pomnilniku", se sklicujemo na ena od dveh stvari: dejanska odstranljiva kartica microSD ali navidezna particija »SDCard« v /data/media. Slednji od teh, praktično gledano, je pravzaprav notranji pomnilnik, vendar ga Googlova konvencija o poimenovanju razlikuje zaradi dejstva, da so ti podatki dostopni uporabniku (na primer, ko je priključen na računalnik).

Trenutno, ko govorimo o "zunanjem pomnilniku", se sklicujemo na ena od dveh stvari: dejanska odstranljiva kartica microSD ali navidezna particija »SDCard« v /data/media.


Zgodovina virtualnih datotečnih sistemov Android

Zdaj, ko se »sdcard« obravnava kot virtualni datotečni sistem, je to pomenilo, da ga je mogoče formatirati kot kateri koli datotečni sistem, ki ga Google želi. Začenši z Nexusom S in Androidom 2.3, se je Google odločil formatirati »sdcard« kot VFAT (virtualni FAT). Ta poteza je bila takrat smiselna, saj bi namestitev VFAT skoraj vsakemu računalniku omogočila dostop do podatkov, shranjenih v vašem telefonu. Vendar sta bili pri tej začetni izvedbi dve veliki težavi.

Prvi zadeva predvsem končnega uporabnika (vas). Če želite napravo povezati z računalnikom, bi za prenos podatkov uporabljali način masovnega shranjevanja USB. To pa je zahtevalo, da naprava Android odklopi navidezno particijo, preden lahko računalnik dostopa do podatkov. Če bi uporabnik želel uporabljati svojo napravo, ko je priključena, bi bilo veliko stvari prikazanih kot nedosegljivih.

The uvedba protokola za prenos medijev (MTP) je rešil to prvo težavo. Ko je priključen, vaš računalnik vašo napravo vidi kot napravo za »medijsko shranjevanje«. Od vašega telefona zahteva seznam datotek, MTP pa vrne seznam datotek, ki jih lahko računalnik prenese iz naprave. Ko se zahteva brisanje datoteke, MTP pošlje ukaz za odstranitev zahtevane datoteke iz pomnilnika. Za razliko od načina masovnega shranjevanja USB, ki dejansko namesti "sdcard", MTP uporabniku omogoča, da še naprej uporablja svojo napravo, ko je priključena. Poleg tega datotečni sistem v telefonu Android ni več pomemben, da računalnik prepozna datoteke v napravi.

Drugič, obstaja dejstvo, da VFAT ni zagotavljal vrste robustnega upravljanja dovoljenj, kot ga je Google potreboval. Mnogi razvijalci aplikacij so na začetku obravnavali »sdcard« kot odlagališče podatkov svojih aplikacij, brez enotnega pojma, kam naj shranijo svoje datoteke. Številne aplikacije bi preprosto ustvarile mapo z imenom svoje aplikacije in vanjo shranile svoje datoteke.

Skoraj vsaka aplikacija v tistem času je zahtevala WRITE_EXTERNAL_STORAGE dovoljenje za pisanje aplikacijskih datotek v zunanji pomnilnik. Vendar pa je bilo bolj zaskrbljujoče dejstvo, da je skoraj vsaka aplikacija zahtevala tudi READ_EXTERNAL_STORAGE dovoljenje - samo za branje lastnih podatkovnih datotek! To je pomenilo, da so lahko aplikacije zlahka dostopale do podatkov, shranjenih kjer koli v zunanjem pomnilniku, in takšno dovoljenje je pogosto odobril uporabnik, ker je bilo potrebno za številne aplikacije funkcijo.

Google je to očitno videl kot problematično. Celotna ideja za upravljanje dovoljenj je ločiti, do katerih aplikacij lahko in do katerih ne morejo dostopati. Če ima skoraj vsaka aplikacija dostop za branje do potencialno občutljivih uporabniških podatkov, je dovoljenje nesmiselno. Tako se je Google odločil, da potrebuje nov pristop. Tu nastopi FUSE.


Datotečni sistem v uporabniškem prostoru (FUSE)

Od različice Android 4.4 se je Google odločil, da navidezne particije »sdcard« ne bo več montiral kot VFAT. Namesto tega je Google začel uporabljati FUSE za posnemanje FAT32 na virtualni particiji »sdcard«. S klicem programa sdcard FUSE za posnemanje dovoljenj imenika v slogu FAT-on-sdcard, lahko aplikacije začnejo dostopati do njegovih podatkov, shranjenih v zunanjem pomnilniku brez potrebe po kakršnih koli dovoljenjih. Dejansko od stopnje API-ja 19 READ_EXTERNAL_STORAGE ni bil več potreben za dostop do datotek, ki se nahajajo v zunanjem pomnilniku – če se podatkovna mapa, ki jo je ustvaril demon FUSE, ujema z imenom paketa aplikacije. VAROVALKA bi obvladala sintetiziranje lastnika, skupine in načinov datotek v zunanjem pomnilniku ko je aplikacija nameščena.

FUSE se razlikuje od modulov v jedru, saj omogoča neprivilegiranim uporabnikom pisanje navideznih datotečnih sistemov. Razlog, da je Google implementiral FUSE, je precej preprost - naredil je, kar so želeli, in je že bil dobro razumljen in dokumentiran v svetu Linuxa. Da citiram a Googlov razvijalec o tem:

»Ker je FUSE prijeten stabilen API, pri prehodu med različicami jedra praktično ni potrebno vzdrževalno delo. Če bi prešli na rešitev v jedru, bi se prijavili za vzdrževanje nabora popravkov za vsako stabilno različico jedra." -Jeff Sharkey, programski inženir pri Googlu

Vendar pa je postajalo povsem jasno, da režijski stroški FUSE med drugimi težavami prinašajo zadetek v zmogljivosti. Razvijalec, s katerim sem govoril o tej zadevi, Michal Kowalczyk, napisal odlično objavo na blogu pred več kot enim letom s podrobnostmi o trenutnih težavah s FUSE. Več tehničnih podrobnosti lahko preberete na njegovem blogu, vendar bom njegove ugotovitve (z njegovim dovoljenjem) opisal bolj laično.


Težava z FUSE

V sistemu Android demon uporabniškega prostora »sdcard« uporablja FUSE za namestitev /dev/fuse v emulirani zunanji pomnilniški imenik ob zagonu. Po tem demon sdcard poišče v napravi FUSE morebitna čakajoča sporočila iz jedra. Če ste poslušali podcast, ste morda slišali gospoda Lemarchanda, da je FUSE uvedel režijske stroške med V/I operacijami - tukaj se v bistvu zgodi.

V resničnem svetu ta zadetek uspešnosti vpliva kaj datoteko, shranjeno v zunanjem pomnilniku.

Problem št. 1 - V/I obremenitev

Recimo, da ustvarimo preprosto besedilno datoteko, imenovano »test.txt«, in jo shranimo v /sdcard/test.txt (ki naj naj vas spomnim, da je /data/media/0/test.txt dejansko ob predpostavki, da je trenutni uporabnik primarni uporabnik na naprava). Če bi želeli prebrati (command cat) to datoteko, bi pričakovali, da bo sistem izdal 3 ukaze: odpri, preberi in nato zapri. Dejansko, kot g. Kowalczyk dokazuje uporabo strace, to se zgodi:

Ker pa se datoteka nahaja v zunanjem pomnilniku, ki ga upravlja demon sdcard, je treba izvesti veliko dodatnih operacij. Po mnenju gospoda Kowalczyka je potrebnih v bistvu 8 dodatnih korakov vsakega od teh 3 posameznih ukazov:

  1. Aplikacija uporabniškega prostora izda sistemski klic, ki ga bo obravnaval gonilnik FUSE v jedru (vidimo ga v prvem izhodu strace)
  2. Gonilnik FUSE v jedru obvesti demona uporabniškega prostora (sdcard) o novi zahtevi
  3. Demon uporabniškega prostora bere /dev/fuse
  4. Demon uporabniškega prostora razčleni ukaz in prepozna delovanje datoteke (npr. odprto)
  5. Demon uporabniškega prostora izda sistemski klic dejanskemu datotečnemu sistemu (EXT4)
  6. Jedro obravnava fizični dostop do podatkov in pošilja podatke nazaj v uporabniški prostor
  7. Uporabniški prostor spremeni (ali ne) podatke in jih ponovno posreduje jedru skozi /dev/fuse
  8. Jedro dokonča izvirni sistemski klic in premakne podatke v dejansko aplikacijo uporabniškega prostora (v našem primeru cat)

To se zdi kot veliko obremenitev samo za en sam V/I ukaz, ki ga je treba izvesti. In imeli bi prav. Da bi to dokazal, je gospod Kowalczyk poskusil z dvema različnima V/I testoma: eden je vključeval kopiranje velike datoteke in drugi kopiranje velike količine majhnih datotek. Primerjal je hitrost FUSE (na virtualni particiji, nameščeni kot FAT32), ki obravnava te operacije, z jedro (na podatkovni particiji, oblikovani kot EXT4), in ugotovil je, da je FUSE dejansko prispeval pomemben nad glavo.

V prvem testu je kopiral 725 MB veliko datoteko v obeh testnih pogojih. Ugotovil je, da implementacija FUSE prenaša velike datoteke 17 % počasneje.

V drugem preizkusu je kopiral 10.000 datotek - vsaka je bila velika 5 KB. V tem scenariju je bila izvedba FUSE končana 40 sekund počasneje kopirati v bistvu 50 MB podatkov.

V resničnem svetu ta zadetek uspešnosti vpliva kaj datoteko, shranjeno v zunanjem pomnilniku. To pomeni aplikacije, kot so Zemljevidi, ki shranjujejo velike datoteke na /sdcard, glasbene aplikacije, ki shranjujejo na tone glasbenih datotek, aplikacije za kamero in fotografije itd. Na vsako V/I operacijo, ki se izvaja in vključuje zunanji pomnilnik, vpliva obremenitev FUSE. Vendar V/I stroški niso edina težava pri FUSE.

Problem #2 - Dvojno predpomnjenje

Predpomnjenje podatkov je pomembno za izboljšanje zmogljivosti dostopa do podatkov. S shranjevanjem bistvenih delov podatkov v pomnilnik lahko jedro Linuxa hitro prikliče te podatke, ko jih potrebuje. Toda zaradi načina implementacije FUSE Android shrani dvojno količino predpomnilnika, ki je potrebna.

Kot dokazuje g. Kowalczyk, se pričakuje, da bo 10 MB velika datoteka shranjena v predpomnilniku kot natanko 10 MB, vendar se namesto tega poveča na velikost predpomnilnika za približno 20 MB. To je problematično pri napravah z manj RAM-a, saj shramba jedra Linuxa uporablja predpomnilnik strani za shranjevanje podatkov v spomin. G. Kowalczyk je preizkusil to težavo z dvojnim predpomnjenjem s tem pristopom:

  1. Ustvarite datoteko z znano velikostjo (za testiranje, 10 MB)
  2. Kopirajte ga v /sdcard
  3. Spustite predpomnilnik strani
  4. Naredite posnetek uporabe predpomnilnika strani
  5. Preberite testno datoteko
  6. Naredite še en posnetek uporabe predpomnilnika strani

Ugotovil je, da je pred njegovim preizkusom jedro uporabljalo 241 MB za predpomnilnik strani. Ko je prebral svojo testno datoteko, je pričakoval, da bo za predpomnilnik strani uporabljenih 251 MB. Namesto tega je ugotovil, da to jedro uporablja 263 MBs za predpomnilnik strani - približno dvakrat več kot je bilo pričakovano. Razlog za to je, ker podatke najprej predpomni uporabniška aplikacija, ki je prvotno izdala V/I klic (FUSE), drugič pa demon sdcard (EXT4 FS).

Problem #3 - Nepopolna implementacija FAT32

Obstajata še dve težavi, ki izhajata iz uporabe FUSE, ki posnema FAT32, ki sta v skupnosti Android manj znani.

Prvi vključuje nepravilni časovni žigi. Če ste kdaj prenesli datoteko (na primer fotografijo) in opazili, da časovni žig ni pravilen, je to zaradi Androidove implementacije FUSE. Ta številka ima obstajal za leta. Natančneje, zadeva vključuje utime() sistemski klic, ki vam omogoča spreminjanje časa dostopa in spreminjanja datoteke. Na žalost klici demona sdcard kot standardni uporabnik nimajo ustreznega dovoljenja za izvedbo tega sistemskega klica. Za to obstajajo rešitve, vendar to zahtevajo od vas imajo root dostop.

Če ste kdaj prenesli datoteko (na primer fotografijo) in opazili, da časovni žig ni pravilen, je to zaradi Androidove implementacije FUSE.

Naslednja težava je bolj zaskrbljujoča za podjetja, ki uporabljajo nekaj podobnega kartico smartSD. Pred FUSE so izdelovalci aplikacij lahko spremljali O_DIRECT zastavica za komunikacijo z vgrajenim mikrokrmilnikom v kartici. S FUSE lahko razvijalci dostopajo samo do predpomnjene različice datoteke in ne morejo videti nobenih ukazov, ki jih pošlje mikrokrmilnik. To je problematično za nekatere poslovne/vladne/bančne aplikacije, ki komunicirajo s karticami microSD z dodano vrednostjo.


Odlaganje FUSE za SDCardFS

Nekateri OEMS so te težave prepoznali že zgodaj in začeli iskati rešitev v jedru, ki bi nadomestila FUSE. Samsung je na primer razvil SDCardFS ki temelji na WrapFS. Ta rešitev v jedru posnema FAT32, tako kot to počne FUSE, vendar se odreče dodatnim V/I, dvojnemu predpomnjenju in drugim težavam, ki sem jih omenil zgoraj. (Da, naj ponovim to točko, ta rešitev, ki jo zdaj izvaja Google, temelji na Samsungovem delu).

Google je sam končno priznal pomanjkljivosti, povezane z FUSE, zato so se začeli premikati k emulacijski plasti FAT32 v jedru, ki jo je razvil Samsung. Podjetje, kot je navedeno v Razvijalci za Android v zakulisju podcast, dela na tem, da bo SDCardFS na voljo za vse naprave v prihajajoči različici jedra. Trenutno lahko vidite njihov napredek delo v AOSP.

Kot Googlov razvijalec je pojasnil prej, je največji izziv pri implementaciji rešitve v jedru, kako preslikati ime paketa v ID aplikacije, potreben za dostop paketa do lastnih podatkov v zunanjem pomnilniku, ne da bi jih potreboval dovoljenja. Toda ta izjava je bila dana pred enim letom in dosegli smo točko, ko ekipa imenuje SDCardFS svojo "naslednjo veliko stvar." To so že potrdili strašna napaka časovnega žiga je bila popravljena, zahvaljujoč odmiku od FUSE, tako da se lahko veselimo vseh sprememb, ki so nastale z opustitvijo FUSE.


Napačne predstave pri preverjanju dejstev

Če ste prišli tako daleč v članek, potem vsa čast, da ste v koraku z vsem do zdaj! Želel sem razjasniti nekaj vprašanj, ki sem jih imel sam, ko sem pisal ta članek:

  • SDCardFS ima nič opraviti z dejanskimi SD karticami. Imenuje se le tako, ker obravnava V/I dostop za /sdcard. In kot se morda spomnite, je /sdcard zastarela oznaka, ki se nanaša na »zunanji« pomnilnik vaše naprave (kjer aplikacije shranjujejo svoje medije).
  • SDCardFS je ni tradicionalni datotečni sistem kot FAT32, EXT4 ali F2FS. To je ovojni datotečni sistem, ki ga je mogoče zlagati in posreduje ukaze nižjim, emuliranim datotečnim sistemom (v tem primeru bi bil to FAT32 na kartici /sdcard).
  • Glede MTP se ne bo spremenilo nič. Še naprej boste uporabljali MTP za prenos datotek v/iz računalnika (dokler Google ne izbere boljšega protokola). Toda vsaj napaka pri časovnem žigu bo popravljena!
  • Kot smo že omenili, ko Google omenja »zunanji pomnilnik«, govori o (za vse namene in namene) notranja /sdcard virtualna particija FAT32 ALI govorijo o dejanski, fizični, odstranljivi microSD kartica. Terminologija je zmedena, vendar nas je to presenetilo.

Zaključek

Z odmikom od FUSE in implementacijo emulacijske plasti FAT32 v jedru (SDCardFS) bo Google zmanjšal znatne dodatne stroške V/I, odpravljanje dvojnega predpomnjenja in reševanje nekaterih nejasnih težav, povezanih z emulacijo FUSE FAT32.

Ker bodo te spremembe narejene v jedru, jih je mogoče uvesti brez večje nove različice Androida poleg tega. Nekateri uporabniki pričakujejo, da bodo te spremembe uradno implementirane v sistemu Android 8, vendar je to mogoče za kakršno koli prihodnjo OTA na napravi Pixel, da bi prinesli jedro Linuxa različice 4.1, na katerem dela Google na.

Za nekatere od vas SDCardFS ni nov koncept. Pravzaprav ga naprave Samsung uporabljajo že leta (navsezadnje so ga oni razvili). Odkar je bil SDCardFS lani predstavljen v AOSP, so se nekateri razvijalci ROM-a in jedra po meri odločili, da ga implementirajo v svoje delo. CyanogenMOD je na neki točki razmišljal, da bi ga implementiral, vendar ga je zavrnil, ko so uporabniki naleteli na težave s svojimi fotografijami. Toda upajmo, da bo Google prevzel oblast nad tem projektom, da bodo uporabniki Androida na vseh prihodnjih napravah lahko izkoristili izboljšave, uvedene z opustitvijo FUSE.