Eellugu
Meil on oma arendatud mĂŒĂŒgiautomaadid. Sees on Raspberry Pi ja veidi lisaplaati. Ăhendatud on mĂŒndiproovija, pangakaardilugeja⊠KĂ”ike haldab ise kirjutatud programm. Kogu töö ajalugu salvestatakse flash-mĂ€lupessa (MicroSD), mis edastatakse seejĂ€rel interneti kaudu (USB-modemi abil) serverisse, kus see sĂ€ilitatakse andmebaasis. MĂŒĂŒgiteave laaditakse 1C-sse, samuti on olemas lihtne veebiliides jĂ€lgimiseks jne.
See tĂ€hendab, et ajakiri on eluliselt vajalik - arvestamiseks (seal on tulu, mĂŒĂŒgid jms), jĂ€lgimiseks (kĂ”iksugu tĂ”rked ja muud eriolukorrad); see on, vĂ”iks öelda, kogu teave, mis meil selle automaadi kohta on.
Probleem
Flash-mĂ€lud osutuvad vĂ€ga ebakindlateks seadmeteks. Need riknevad ĂŒpris regulaarselt. See toob kaasa nii automaatide seiskumisi kui ka (kui mingil pĂ”hjusel ei saanud ajakirja online edastada) andmete kadumise.
See ei ole esimene kogemus flash-mĂ€lude kasutamisest, enne seda oli teistsugune projekt, kus oli rohkem kui sada seadet, kus ajalugu hoiti USB-flash-mĂ€ludel, seal olid samuti usaldusvÀÀrsuse probleemid, mĂ”nikord oli kuu jooksul riknenud mĂ€lude arv kĂŒmnetes. Proovisime erinevaid flash-mĂ€lusid, sealhulgas ka SLC mĂ€luga brĂ€nditud, jah, mĂ”ned mudelid on usaldusvÀÀrsemad kui teised, kuid flash-mĂ€lude vahetamine ei lahendanud probleemi kardinaalselt.
TĂ€helepanu! Pikem lugemine! Kui teid ei huvita âmiksâ, vaid ainult âkuidasâ, vĂ”ite kohe minna artiklist.
Lahendus
Esimene mÔte, mis tuleb meelde: loobuda MicroSD-st, panna nÀiteks SSD ja laadida sealt. Teoreetiliselt on see vÔib-olla vÔimalik, kuid suhteliselt kallis ja mitte vÀga usaldusvÀÀrne (lisandub USB-SATA adapter; odavate SSD-de riketestatistika ei ole samuti julgustav).
USB HDD ei tundu samuti eriti atraktiivne lahendus.
SeetĂ”ttu jĂ”udsime sellise variandini: jĂ€tta laadimine MicroSD-lt, kuid kasutada neid read-only reĆŸiimis, ning töö ajalugu (ja muu konkreetse seadme jaoks unikaalne teave - seerianumber, sensorite kalibreerimine jne) hoida kuskil mujal.
Read-only failisĂŒsteemide teema Raspberry Pi jaoks on juba pĂ”hjalikult uuritud, ma ei hakka selle artikli raames detailidele peatuma. (kuid kui huvi on - vĂ”ib-olla kirjutan selle teema kohta mini-artikli). Ainuke punkt, mida tahaksin mĂ€rkida: nii isikliku kogemuse kui ka juba rakendanud tagasiside pĂ”hjal on paranduste usaldusvÀÀrsuses olemas. Jah, tĂ€ielikult rikete eest pÀÀseda ei saa, kuid nende sagedust on tĂ€iesti reaalne oluliselt vĂ€hendada. Ja ka kaartide vormingud muutuvad ĂŒhtseks, mis muudab teenindava personali asenduse oluliselt lihtsamaks.
Riistvara
MĂ€lu tĂŒĂŒbi valik ei tekitanud erilisi kahtlusi â NOR Flash.
Argumentid:
- lihtne ĂŒhendamine (enamasti SPI buss, mille kasutamise kogemus on juba olemas, nii et âraudaâ probleeme ei paista tulevat);
- naljakas hind;
- standardne tööprotokoll (rakendus on juba Linuxi tuumas olemas, soovi korral vÔib vÔtta ka kolmanda osapoole lahenduse, mida on samuti saadaval, vÔi isegi kirjutada oma, hea, et kÔik on lihtne);
- usaldusvÀÀrsus ja ressurss:
tĂŒĂŒpilisest andmelehelt: andmed sĂ€ilitab 20 aastat, 100000 kustutamistsĂŒklit iga bloki kohta;
vÀlistest allikatest: ÀÀrmiselt madal BER, vÀidetakse, et vea parandamise koodide vajadust ei ole (mÔnedes teadustes kÀsitletakse ECC-d NOR-i jaoks, kuid tavaliselt peetakse seal silmas MLC NOR-i, nii et selline asi juhtub ka).
Hinnatakse nÔudeid mahu ja ressursi osas.
Soov on, et andmed sĂ€ilitaksid garantii korral mitu pĂ€eva. See on vajalik selleks, et igasuguste sideprobleemide korral ei kaoks mĂŒĂŒgihaja ajalugu. Suundume 5 pĂ€eva poole, mille jooksul (isegi arvestades nĂ€dalavahetusi ja pĂŒhi) probleemi lahendada.
Meil koguneb nĂŒĂŒd ööpĂ€evas umbes 100 kb logi (3-4 tuhat kirje), kuid jĂ€rk-jĂ€rgult see number kasvab â detailide arv suureneb, lisanduvad uued sĂŒndmused. Lisaks esinevad mĂ”nikord puhangud (mĂ”ni sensor hakkab valehĂ€iretega spammima, nĂ€iteks). Arvestagem 10 tuhande kirje peale 100 baiti â megabait pĂ€evas.
Seega tuleb kokku 5 MB puhast (hÀsti pigistatavat) andmeid. Nendele veel (harjumuspÀrane hinnang) 1 MB teenindusandmeid.
Seda silmas pidades on meie vajadus mikroskeemi jĂ€rele 8 MB, kui me ei kasuta kokkusurumist, vĂ”i 4 MB, kui me seda teeme. Need on tĂ€iesti reaalsed numbrid selle tĂŒĂŒpi mĂ€lu jaoks.
Mida ressurssi puutub: kui planeerime, et mĂ€lu kirjutatakse tĂ€ielikult ĂŒle mitte sagedamini kui iga 5 pĂ€eva tagant, siis 10-aastase teenuse jooksul saame vĂ€hem kui tuhat ĂŒle kirjutamise tsĂŒklit.
Tuletan meelde, et tootja lubab sada tuhat.
Veidi NOR vs NAND-ist
TÀna on muidugi NAND mÀlu tunduvalt populaarsem, kuid selle projekti jaoks ma seda kasutada ei soovitaks: NAND, erinevalt NORist, nÔuab kindlasti vigade parandamise koode, rikkeplokkide tabeleid jne., ja NAND mikrokiipide jalgade arv on tavaliselt samuti suurem.
NORi puuduseks vÔib mÀrkida:
- vÀike maht (ja seega kÔrge hind megabaiti kohta);
- madal edastuskiirus (peamiselt seetÔttu, et kasutatakse jÀrjestikku liidest, tavaliselt SPI vÔi I2C);
- aeglane kustutamine (sÔltuvalt ploki suurusest vÔtab see mÔnest osast sekundist kuni mitme sekundini).
Tundub, et miski pole kriitiline, nii et jÀtkame.
Kui detailid huvitavad, on valitud mikrokiip (kuigi see ei oma suurt tĂ€htsust, on turul palju analooge, mis on ĂŒhenduste ja kĂ€skude osas ĂŒhilduvad; isegi kui tahame kasutada teise tootja mikrokiipi ja/vĂ”i teise mahtu, töötab see koodi muutmata).
Kasutangi Linuxi tuuma sisse ehitatud draiverit, Raspberryl, tĂ€nu seadmepuu overlaytoetusele, on kĂ”ik vĂ€ga lihtne â tuleb panna /boot/overlaysse kompileeritud overlay ja teha veidi muudatusi /boot/config.txt.
NĂ€ide dts failist
Austusega öeldes ei ole ma kindel, et see on kirjutatud vigadeta, kuid see töötab.
/*
* Device tree overlay for at25 at spi0.1
*/
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709";
/* disable spi-dev for spi0.1 */
fragment@0 {
target = <&spi0>;
__overlay__ {
status = "okay";
spidev@1{
status = "disabled";
};
};
};
/* the spi config of the at25 */
fragment@1 {
target = <&spi0>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
flash: m25p80@1 {
compatible = "atmel,at25df321a";
reg = <1>;
spi-max-frequency = <50000000>;
/* default to false:
m25p,fast-read ;
*/
};
};
};
__overrides__ {
spimaxfrequency = <&flash>,"spi-max-frequency:0";
fastread = <&flash>,"m25p,fast-read?";
};
};Ja veel ĂŒks rida config.txt-s
dtoverlay=at25:spimaxfrequency=50000000Mikrokiibi ĂŒhendamise kirjelduse vahele jĂ€tan. Ăhest kĂŒljest ei ole ma elektroonika spetsialist, teisest kĂŒljest on see minu jaoks isegi liiga lihtne: mikrokiibil on vaid 8 jalga, millest me vajame maapinda, toiteallikat ja SPI-d (CS, SI, SO, SCK); tasemed kattuvad Raspberry Pi omadega, tĂ€iendavat toitekaablit ei ole vaja â lihtsalt ĂŒhendage kuus mainitud kontakti.
Ălesande seadmine
Nagu tavaliselt, lĂ€bib ĂŒlesande seadmine mitu iteratsiooni, ma arvan, et on aeg jĂ€rgmise jaoks. Nii et peatugem, kogume kokku selle, mis on juba kirjutatud, ja selgitame vĂ€lja varjatud detailid.
Nii oleme otsustanud, et ajakiri hoitakse SPI NOR Flashis.
Mis on NOR Flash, neile, kes ei tea
See on energiateadlik mÀlu, millega saab teha kolme operatsiooni:
- Lugemine:
Tavaline lugemine: edastame aadressi ja loeme nii palju baite, kui vajame; - Salvestamine:
NOR flash'i kirjutamine nĂ€ib olevat tavaline, kuid sel on ĂŒks eripĂ€ra: saab ainult muuta 1-ks 0, kuid mitte vastupidi. NĂ€iteks, kui meie mĂ€lu rakus oli 0x55, siis pĂ€rast 0x0f kirjutamist salvestatakse seal juba 0x05. (vt allolevat tabelit); - Kustutamine:
Muidugi peame suutma teha ka vastupidist operatsiooni â muuta 0-ks 1, just selleks on olemas kustutamise operatsioon. Erinevalt esimestest kahest, toimib see mitte baitidega, vaid plokkidega (valitud mikroskeemi minimaalne kustutusplokk on 4 kB). Kustutamine hĂ€vitab kogu ploki ja see on ainus viis 0 muuta 1-ks. SeetĂ”ttu tuleb flash-mĂ€luga töötades sageli andmestruktuure joondada kustutusploki piirile.
NOR Flash'i kirjutamine:
Binaarsed andmed
See oli
01010101
Kirjutasime
00001111
Muutus
00000101
Ise ajakiri esindab muutuva pikkusega kirjeid. TĂŒĂŒpiline kirje pikkus on umbes 30 baiti (kuigi mĂ”nikord vĂ”ib esineda ka mitme kilobaidi pikkuseid kirjeid). Selles osas töötame nendega lihtsalt kui baitide kogumiga, kuid kui on huvitav, siis kirjete sees kasutatakse CBOR'i.
Lisaks ajakirjale peame hoidma teatud âseadistamiseâ teavet, nii uuendatavat kui ka mitte: mingi seadme ID, andurite kalibreerimine, lipp âseade on ajutiselt vĂ€ljalĂŒlitatudâ, jne.
See teave esindab vÔtme-vÀÀrtuse kirjeid, mis samuti salvestatakse CBOR'is. Meil ei ole seda teavet vÀga palju (kuni paar kilobaiti), see uuendatakse harva.
Edaspidi nimetame seda kontekstiks.
Kui meenutada, millest see artikkel algas, on vÀga oluline tagada andmete usaldusvÀÀrne hoidmine ja, kui vÔimalik, katkematu töö ka seadme riketel vÔi andmekahjustusel.
Milliseid probleemide allikaid saaks kaaluda?
- Toite katkestamine kirjutamise/kustutamise operatsioonide ajal. See on nagu âmiks mitte paljastada vastupidiâ.
Teave aadressilt stackexchange'ist: toite katkestamise korral flash-mĂ€lu töötamise ajal nii kustutamine (1-ks seadmine) kui ka kirjutamine (0-ks seadmine) toovad kaasa mÀÀratlemata kĂ€itumise: andmed vĂ”ivad kirjutada, kirjutada osaliselt (ĂŒtleme, et edastasime 10 baiti/80 bitti, kuid jĂ”udsime kirjutada ainult 45 bitti), samuti ei saa vĂ€listada, et osa bite vĂ”ib olla âvahepealsesâ seisundis (lugemine vĂ”ib anda nii 0 kui ka 1); - Flash-mĂ€lu enda vead.
BER on kuigi vÀga madal, kuid ei tohi olla null; - Bussi vead
SPI kaudu edastatavad andmed ei ole mingil viisil kaitstud, vĂ”ivad tekkida nii ĂŒksikud bitivead kui ka sĂŒnkroonimisvead â bitide kaotus vĂ”i lisamine (mis viib massiliste andmete moonutusteni); - Muud vead/rikkumised
Koodivead, Raspberry 'glitchid', tulnukate sekkumineâŠ
Olen sÔnastanud nÔuded, mille tÀitmine on minu arvates vajalik usaldusvÀÀrsuse tagamiseks:
- salvestused peavad minema kohe mĂ€lupulgale, edasilĂŒkatud salvestust ei arvestata; - kui viga tekib, siis peab see avastatama ja töötlemine toimuma vĂ”imalikult varakult; - sĂŒsteem peab vĂ”imalusel taastama töö pĂ€rast vigu.
(nĂ€ide elust "kuidas ei peaks olema", millega, arvan, on kĂ”ik kokku puutunud: pĂ€rast avariilist taaskĂ€ivitamist "korrastati" failisĂŒsteem ja operatsioonisĂŒsteem ei kĂ€ivitu)
Ideed, lÀhenemised, mÔtted
Kui hakkasin sellele ĂŒlesandele mĂ”tlema, liikusid mu peas ringi hulgaliselt ideid, nĂ€iteks:
- kasutada andmete tihendamist;
- kasutada nutikaid andmestruktuure, nÀiteks hoida salvestuste pÀiseid eraldi iseenesest salvestustest, et vigade korral saaksid teised andmed probleemideta lugeda;
- kasutada bitifiltreid salvestuse lĂ”petamise kontrollimiseks toite vĂ€ljalĂŒlitamisel;
- hoida kontrollsummasid kÔigi ja kÔige kohta;
- kasutada mingisugust vigadele vastupidavat kodeerimist.
Osad neist ideedest olid kasutusel, mÔnel oli otsustanud loobuda. Vaatame jÀrjestikku.
Andmete kokkusurumine
Isiklikud sĂŒndmused, mida me logis salvestame, on piisavalt ĂŒhesugused ja korduvad ("viskasime 5 rubla mĂŒndi", "vajutasime tagasiandmise nuppu", ...). SeetĂ”ttu peaks tihendamine olema piisavalt tĂ”hus.
Tihendamise kulud on ebaolulised (meie protsessor on piisavalt vĂ”imas, isegi esimesel Pi-l oli ĂŒks tuum 700 MHz-l, tĂ€napĂ€evastes mudelites on mitu tuuma, mille sagedus ĂŒletab gigahertsi), andmesalvestuse vahetuskiirus ei ole kĂ”rge (mĂ”ned megabait sekundis), salvestuste suurus on vĂ€ike. Ăldiselt, kui tihendamine mingil moel jĂ”udlust mĂ”jutab, siis ainult positiivselt. (absoluutselt mitte kriitiline, lihtsalt konstateerin). Plus meil ei ole ju pĂ€ris embedded, vaid tavaline Linux â seega implementatsioon ei peaks nĂ”udma palju pingutust (piisab lihtsalt raamatukogude lingimisest ja mĂ”ne funktsiooni kasutamisest sealt).
VĂ”eti tĂŒkk logifailist töötavast seadmest (1,7MB, 70 tuhat kirje) ja alguses kontrolliti selle kokkusurumisvĂ”imet olemasolevate programmide gzip, lz4, lzop, bzip2, xz, zstd abil.
- gzip, xz, zstd nÀitasid sarnaseid tulemusi (40KB).
Ăllatas, et trendikas xz nĂ€itas end siin gzipi vĂ”i zstd tasemel; - lzip vaikeseadetega andis veidi halvemad tulemused;
- lz4 ja lzop nÀitasid mitte eriti head tulemust (150KB);
- bzip2 nĂ€itas ĂŒllatavalt head tulemust (18KB).
Nii et andmed on vÀga hÀsti tihendatavad.
Seega (kui me ei leia fataalseid puudusi) tihendamine peaks toimuma! Lihtsalt sellepÀrast, et sama mÀlupulgale mahtuda rohkem andmeid.
MÔelgem puudustele.
Esimene probleem: me oleme juba kokku leppinud, et iga kirje peab viivitamatult minema mÀlupulgale. Tavaliselt kogub arhiveerija andmeid sisendvoogudest kuni otsustab, et on aeg kirjutada vÀljundisse. Meie peame aga kohe saama tihendatud andmepaketi ja salvestama selle mitte-volatile mÀllu.
NĂ€en kolme teed:
- Tihendada iga kirje sÔnaraamatutihenduse abil, mitte eelnevalt kÀsiteldu algoritmide kaudu.
Töötav variant, kuid see ei meeldi mulle. Teatud kokkusurumise taseme tagamiseks peab sĂ”naraamat olema âteravdatudâ konkreetsete andmete jaoks, mis tahes muudatus toob kaasa kokkusurumise taseme katastroofilise languse. Jah, probleem lahendatakse uue versiooni sĂ”naraamatu loomisega, kuid see on peavalu â me peame hoidma kĂ”iki sĂ”naraamatu versioone; iga kirje puhul peame mĂ€rkima, millise sĂ”naraamatu versiooniga see on tihendatud⊠- Tihendada iga kirje âklassikalisteâ algoritmide abil, kuid sĂ”ltumatult teistest.
KĂ€sitletavad kokkusurumise algoritmid ei ole mĂ”eldud selliste suurte kirjetega töötamiseks (kĂŒmned baitid), tihenduskoefitsient on ilmselt vĂ€iksem kui 1 (st andmete mahu suurenemine, mitte kokkusurumine); - Teha FLUSH pĂ€rast iga kirjet.
Paljuski tihendamisraamatukogudes on FLUSH-i tugi. See on kĂ€sk (vĂ”i parameeter tihendamisprotseduurile), mille saades arhiveerija loob tihendatud voog, et sellest lĂ€htudes saaks taastada kĂ”ik tihendamata andmed, mis on juba saadud. Selline analoogsyncfailisĂŒsteemides vĂ”icommitSQLis.
Oluline on, et jÀrgnevad tihendamisoperatsioonid saavad kasutada akumuleeritud sÔnastikku ning tihendusaste ei kannata nii palju nagu eelnevas variandi puhul.
MÔtlen, et on ilmselge, et valisin kolmanda variandi, peatume sellel lÀhemalt.
Löydettiin FLUSH-ist zlib-is.
Tehtud inspiratsiooniks artiklist, tegin katse, vÔttes 70 tuhat pÀringut pÀringute logidest reaalsetelt seadmetelt, kui lehe suurus on 60 KB (lehe suuruse juurde me veel naaseme) sain:
Algandmed
gzip tihendamine -9 (ilma FLUSHita)
zlib Z_PARTIAL_FLUSH-iga
zlib Z_SYNC_FLUSH-iga
Maht, KB
1692
40
352
604
Esmapilgul tundub, et FLUSH-i hind on ĂŒlemÀÀra kĂ”rge, kuid tegelikult on meie valik vaene â kas mitte ĂŒldse tihendada vĂ”i tihendada (ja vĂ€ga efektiivselt) FLUSH-iga. Ărge unustage, et meil on 70 tuhat kirjet, Z_PARTIAL_FLUSH-i toodu ĂŒleliigusus on ainult 4-5 baiti kirje kohta. Tihenduskoefitsient osutus peaaegu 5:1, mis on suurepĂ€rane tulemus.
VĂ”ib tunduda ĂŒllatav, kuid tegelikult on Z_SYNC_FLUSH efektiivsem viis FLUSH-i tegemiseks.
Kui kasutada Z_SYNC_FLUSH-i, on iga kirje nelja viimast baiti alati 0x00, 0x00, 0xff, 0xff. Kui need on meile teada â siis me ei pea neid salvestama, seega on lĂ”plik suurus vaid 324 KB.
Artiklis, millele viitan, on seletus:
Uus tĂŒĂŒp 0 blokk tĂŒhjade sisudega lisatakse.
TĂŒĂŒp 0 blokk tĂŒhjade sisudega koosneb:
- kolmest bitist koosnevast ploki pÀisest;
- 0 kuni 7 bitti, mis on vÔrdsed nulliga, et saavutada baitide joondamine;
- neljast baitist koosnevast jÀrjestusest 00 00 FF FF.
Nagu ei ole keeruline mÀrgata, esimeses plokis enne neid 4 baiti on 3 kuni 10 nullbitid. Kuid praktika on nÀidanud, et nullbitte on tegelikult vÀhemalt 10.
Selgub, et nii lĂŒhikesed andmeplokid kodeeritakse tavaliselt (alati?) ploki tĂŒĂŒp 1 (fikseeritud plokk), mis kindlasti lĂ”ppeb 7 nullbiti, kokku 10-17 garanteeritud nullbitti (ja ĂŒlejÀÀnud on nullid umbes 50% tĂ”enĂ€osusega).
Seega 100% juhtudel testandmetel on enne 0x00, 0x00, 0xff, 0xff ĂŒks nullbait, ja rohkem kui kolmandal juhul â kaks nullbaidi. (vĂ”ib-olla on asi selles, et kasutan binaarset CBOR-i, ja tekstilisel JSON-il oleksime vĂ”inud sagedamini kohata tĂŒĂŒp 2 plokke â dĂŒnaamiline plokk, vastavalt oleksime kohanud plokke ilma lisanduvate nullbaitideta enne 0x00, 0x00, 0xff, 0xff).
KokkuvÔttes on olemasolevate testandmete pÔhjal vÔimalik mahtuda alla 250 KB tihendatud andmeid.
Saame veel natukene kokku hoida, tegeledes bittide ĆŸongleerimisega: praegu ignoreerime mitme nullbiti olemasolu ploki lĂ”pus, samuti ei muutu mĂ”ned bitid ploki alguses...
Aga siis tegin ma tahteotsuse peatuda, muidu vÔin sellise tempoga jÔuda oma arhiiveri loomiseni.
KokkuvĂ”ttes sain oma testandmetelt 3-4 baiti kirjutamiseks, kompressioonikordaja oli ĂŒle 6:1. Ausalt öeldes: ma ei oodanud sellist tulemust, minu arvates on kĂ”ik, mis on parem kui 2:1 â juba tulemus, mis Ă”igustab kompressiooni kasutamist.
KÔik on suurepÀrane, kuid zlib (deflate) on ikkagi arkaalne, tuntud ja veidi vanamoeline kompressioonialgoritm. Ainult see, et sÔnaraamatuks kasutatakse viimaseid 32Kb tihendamata andmeid, tundub tÀna kummaline (st kui mÔni andmeplokk sarnaneb sellele, mis oli sisendis 40Kb tagasi, siis hakkab see uuesti arhiivima, mitte ei viita varasemale esinemisele). Moodne kaasaegne arhiiver mÔÔdab sÔnaraamatu suurust tihti megabaitides, mitte kilobaitides.
Nii et jĂ€tkame meie mini-uuringut arhiivide ĂŒle.
J jÀrgmiseks prooviti bzip2 (tuletan meelde, et ilma FLUSHita nÀitas see fantastilist tihendust, peaaegu 100:1). Kahjuks FLUSHiga ei andnud ta end hÀsti tunda, tihendatud andmete suurus oli suurem kui tihendamata.
Minu oletused ebaÔnnestumise pÔhjustest
Libbz2 pakub ainult ĂŒhte flush-varianti, mis tundub olevat sĂ”naraamatu puhastamine (analoog Z_FULL_FLUSH zlibis), rÀÀkida mingist tĂ”husast tihendamisest pĂ€rast seda ei saa.
Viimaseks prooviti zstd. Olenevalt parameetritest tihendab ta kas gzipi tasemel, kuid palju kiiremini, vÔi paremini kui gzip.
Kahjuks FLUSHiga ei andnud ka tema end âvĂ€ga hĂ€stiâ: tihendatud andmete kogusumma oli umbes 700Kb.
Mina projekti lehe kaudu githubis, sain vastuse, et tuleb arvestada kuni 10 baiti haldusandmeid iga tihendatud andmeploki kohta, mis on lÀhedal saadud tulemustele, deflate'i mitte mingil juhul ei saavuta.
Sellega otsustasin arhiivide katsetamisel peatuda (tuletan meelde, et xz, lzip, lzo, lz4 ei nÀidanud ennast veel FLUSHi testimise etapis, ja ma ei hakanud arvestama eksootiliste tihendamisalgoritmidega).
Naaseme arhiivimise probleemide juurde.
Teine probleem (nagu öeldakse jĂ€rjestuse, mitte tĂ€henduse jĂ€rgi) on, et kokkusurutud andmed esindavad ĂŒhtset voogu, kus pidevalt viidatakse eelnevatele segmentidele. Seega, kui mĂ”ni kokkusurutud andmete segment on kahjustatud, kaotame mitte ainult seotud lahtised andmed, vaid ka kĂ”ik jĂ€rgnevad.
Selle probleemi lahendamiseks on mitu lÀhenemist:
- Probleemi ennetamine â lisada kokkusurutud andmetesse ĂŒleliigsust, mis vĂ”imaldab vigu tuvastada ja parandada; sellest rÀÀgime hiljem;
- Minimeerida tagajÀrjed hÀire tekkimisel.
Oleme varem rÀÀkinud, et iga andmeploki saab kokku suruda sĂ”ltumatult, mis tĂ€hendab, et probleem kaob iseenesest (ĂŒhe bloki andmete rikkumine viib ainult selle bloki andmete kaotamiseni). Siiski on see ÀÀrmuslik olukord, kus andmete kokkusurumine osutub ebaefektiivseks. Teine ÀÀrmus: kasutada kĂ”iki 4MB meie kiipi kui ĂŒhte arhiivi, mis annab meile suurepĂ€rase kokkusurumise, kuid katastroofilised tagajĂ€rjed andmete rikkumise korral.
Jah, usaldusvÀÀrsuse osas on vajalik kompromiss. Kuid tuleb meeles pidada, et me töötame vÀlja andmete salvestamise formaati energiat mitte vajavas mÀlus, millel on ÀÀrmiselt madal BER ja deklareeritud andmete sÀilitamise aeg 20 aastat.
Eksperimentide kÀigus avastasin, et mÀrgatavad kokkusurumise taseme kaotused algavad alla 10KB suurustes kokkusurutud andmeplokkides.
Varem mainiti, et kasutatav mĂ€lu on lehekĂŒljeline, ma ei nĂ€e pĂ”hjust, miks mitte kasutada vastavust âĂŒks lehekĂŒlg â ĂŒks kokkusurutud andmeplokkâ.
Seega on minimaalne mÔistlik lehe suurus 16KB (koos varuga teenindusteabe jaoks). Siiski seab nii vÀike lehe suurus mÀrkimisvÀÀrseid piiranguid maksimaalse andmereservi suurusele.
Kuigi ma ei prognoosi hetkel suuremaid kui ĂŒhes kilobaidis kokkusurutud salvestusi, otsustasin kasutada 32KB suuruseid lehti (kokku 128 lehte kiibi kohta).
KokkuvÔte:
- Salvestame andmed kokkusurutud kujul zlib (deflate) abil;
- Iga salvestuse jaoks seadistame Z_SYNC_FLUSH;
- Igal kokkusurutud salvestusel lÔikame Àra lÔppbaidid, (nÀiteks 0x00, 0x00, 0xff, 0xff); pealkirjas nÀitame, kui palju baite oleme lÔiganud;
- Andmed salvestatakse lehtedena 32 kb kaupa; lehe sees on ĂŒhtne kokkupressitud andmevoog; iga lehe jaoks alustame kokkupressimist uuesti.
Ja enne, kui lÔpetame kokkupressimise, tahaksin juhtida tÀhelepanu sellele, et meil Ônnestub kokkupressida vaid paar baitit salvestuse kohta, seega on ÀÀrmiselt oluline mitte puhtust teavet paisutada, iga bait on siin arvesse vÔetud.
Andmepealkirjade salvestamine
Kuna meil on muutuva pikkusega salvestused, peame kuidagi mÀÀrama salvestuste asukoha/piirid.
Ma tean kolme lÀhenemist:
- KÔik salvestused on salvestatud katkematusse voogu, kÔigepealt tuleb salvestuse pealkiri, mis sisaldab pikkust, ja seejÀrel endine salvestus.
Selles variandis vÔivad nii pealkirjad kui ka andmed olla muutuva pikkusega.
Sisuliselt on meil ĂŒhekĂŒlgne loend, mida kasutatakse pidevalt; - Pealkirjad ja salvestused ise on salvestatud eraldi voogudes.
Kasutades kindla pikkusega pealkirju, saavutame selle, et ĂŒhe pealkirja rike ei mĂ”juta teisi.
Sarnast lĂ€henemist kasutatakse nĂ€iteks paljudes failisĂŒsteemides; - Salvestused on salvestatud katkematusse voogu, salvestuse piir mÀÀratakse teatud mĂ€rgiste (sĂŒmbolid/sĂŒmbolite jada, mis on andmeblokis keelatud) jĂ€rgi. Kui salvestuses leidub marker, siis asendame selle teatud jadaga (ekreerime selle).
Sarnast lÀhenemist kasutatakse nÀiteks PPP protokollis.
Illustreerin.
Variant 1:

Siin on kÔik vÀga lihtne: teades salvestuse pikkust, saame arvutada jÀrgmise pealkirja aadressi. Nii liigume pealkirjade vahel, kuni leiame 0xFF-ga tÀidetud ala (vaba ala) vÔi lehe lÔpuni.
Variant 2:

Muutuva pikkuse tĂ”ttu ei saa me eelnevalt öelda, kui palju salvestusi (seega ka pealkirju) meil lehte lĂ€heb. Saame pealkirjad ja andmed jaotada erinevatele lehtedele, kuid mulle meeldib teise lĂ€henemise pooldasem: nii pealkirjad (kindla suurusega) on lehe alguses ja andmed (muutuva pikkusega) lehe lĂ”pust. Kui nad âkohtuvadâ (vaba ruumi ei piisa uue salvestuse jaoks) â loeme selle lehe tĂ€idetuks.
Variant 3:

Siin pole vajadust hoida pÀises pikkust vÔi muid andmete asukoha kohta kÀivaid andmeid, piisab markeritest, mis tÀhistavad kirjete piire. Siiski tuleb andmeid töödelda kirjutamisel/loodud.
Markerina kasutaksin 0xff (millega leht on tÀidetud pÀrast kustutamist), seega ei tÔlgendata vabatahtlikku ala tÔeks.
VÔrdlustabel:
Variant 1
Option 2
Variant 3
Vigade taluvus
â
+
+
Kompaktsus
+
â
+
Rakendamise keerukus
*
**
**
Esimesel variandil on surmav puudus: kui mĂ”ni pĂ€is on kahjustatud, siis hĂ€vib kogu jĂ€rgmine ahel. ĂlejÀÀnud variandid vĂ”imaldavad taastada osa andmeid isegi massiliste kahjustuste korral.
Kuid siinkohal on asjakohane meenutada, et otsustasime andmed hoida kokkusurutud kujul, nii et me kaotame kÔik andmed lehe peal pÀrast "rikkuvat" kirje, seega, kuigi tabelis on miinus, ei arvesta me seda.
Kompaktsus:
- esimeses variandis peame pĂ€ises hoidma ainult pikkust, kui kasutada tĂ€isarve muutuva pikkusega, siis enamikul juhtudel piisab ĂŒhest baitist;
- teises variandis peame hoidma algset aadressi ja pikkust; kirje peaks olema pideva suurusega, hindan 4 baiti kirje kohta (kaks baiti nihke jaoks ja kaks baiti pikkuse jaoks);
- kolmandale variandile piisab vaid ĂŒhest sĂŒmbolist kirje alguse tĂ€histamiseks, pluss ise kirje tĂ€nu ekraanile suureneb 1-2%. Ăldiselt on ligikaudne pariteet esimese variandiga.
Alguses kaalusin teist varianti pÔhjalikult (ja isegi kirjutasin teostuse). LÔpetasin selle, kui otsustasin lÔpuks kasutada kokkusurumist.
VĂ”imalik, et kunagi kasutan sarnast varianti. NĂ€iteks, kui pean tegelema andmete salvestamisega laeva jaoks, mis reisib Maa ja Marsi vahel â tĂ€iesti teistsugused nĂ”udmised usaldusvÀÀrsusele, kosmiline kiirgus, âŠ
Mis puutub kolmandasse variandisse: panin sellele kaks tĂ€hte rakendamise keerukuse tĂ”ttu, lihtsalt seetĂ”ttu, et ma ei armasta ninaga tegelemist ekraanide, pikkuste muutmiseks jne. Jah, vĂ”ib-olla olen kallutatud, kuid koodi pean ma ikkagi kirjutama â miks sundida end tegema seda, mis ei meeldi.
KokkuvĂ”te: valime salvestusvariandi kujul ahelad "pĂ€is pikkusega â muutuva pikkusega andmed" efektiivsuse ja rakendamise lihtsuse tĂ”ttu.
BitivÀljade kasutamine kirje kirjutamise operatsioonide eduka kontrollimise jaoks
Ma ei mÀleta enam, kust ma selle idee sain, aga see nÀeb enam-vÀhem vÀlja nii:
Iga kirje jaoks eraldame mitu bitti lipu salvestamiseks.
Kuidas me varem rÀÀkisime, pĂ€rast eraseâit on kĂ”ik bitid tĂ€idetud 1-ga ja saame muuta 1 0-ks, aga mitte vastupidi. Seega kasutame "lipp pole seadistatud" jaoks 1 ja "lipp on seadistatud" jaoks 0.
Nii vÔib vÀlja nÀha muutuva pikkusega kirje paigutamine flash-mÀlu:
- Seame lipu "kirje pikkus algas";
- Salvestame pikkuse;
- Seame lipu "andmete salvestamine algas";
- Salvestame andmed;
- Seame lipu "salvestamine lÔppes".
Lisaks on meil "tekkis viga" lipp, kokku 4 bitilist lippu.
Sellisel juhul on meil kaks stabiilset olekut "1111" â salvestamine ei alanud ja "1000" â salvestamine Ă”nnestus; ootamatul salvestamisprotsessi katkestamisel saame vahepealsed olekud, mille saame hiljem tuvastada ja kĂ€sitleda.
LÀhenemine on huvitav, kuid see kaitseb ainult ootamatute toitekatkestuste ja sarnaste tÔrgete eest, mis on muidugi tÀhtis, kuid see ei ole kaugeltki ainus (ja isegi mitte peamine) vÔimalik tÔrgete pÔhjus.
KokkuvÔte: Liigume edasi, otsides head lahendust.
Kontrollsummad
Kontrollsummad pakuvad ka vÔimalust veenduda (piisava tÔenÀosusega), et loeme just seda, mis pidi olema salvestatud. Ja erinevalt eelnevalt arutatud bitivÀljadest töötavad nad alati.
Kui vaadata potentsiaalsete probleemide loetelu, millest me rÀÀkisime, suudab kontrollsumma tuvastada vea sĂ”ltumata selle pĂ€ritolust (v.a. vĂ”ib-olla pahatahtlikud vĂ€lismaali inimesed â need vĂ”ivad ka kontrollsummat valehĂ€irega petta).
Seega, kui meie eesmÀrk on kontrollida, et andmed on terved, on kontrollsummad suurepÀrane mÔte.
Kontrollsummade arvutamise algoritmi valik ei tekitanud kĂŒsimusi â CRC. Ăhelt poolt vĂ”imaldavad matemaatilised omadused 100% tuvastada teatud tĂŒĂŒpi vigu, teiselt poolt nĂ€itab see algoritm juhuslike andmete puhul tavaliselt kokkulangevuste tĂ”enĂ€osust mitte oluliselt suurem kui teoreetiline piir.
Olgu see mitte kĂ”ige kiirem algoritm, mitte alati minimaalne kokkupĂ”rgete arvu poolest, kuid tal on ĂŒks vĂ€ga oluline omadus: testides, millega olen kokku puutunud, ei ole olnud mustreid, kus see selgelt ebaĂ”nnestuks. Stabiilsus on antud juhul peamine kvaliteet.
Rohke uurimistöö nÀide: , (linkidena narod.ru, vabandust).
Siiski ei ole kontrollsummade valimise ĂŒlesanne veel lĂ”ppenud, CRC on terve kontrollsummade perekond. Tuleb otsustada pikkuse ĂŒle ja seejĂ€rel valida polĂŒnoom.
Kontrollsummade pikkuse valik ei ole nii lihtne kĂŒsimus, kui esmapilgul tundub.
Illustreerime:
Olgu meil veavoodus iga bait
ja ideaalne kontrollsumma, arvutame keskmise veahulga miljoni kirje kohta:
Andmed, bait
Kontrollsumma, bait
Tuvastamata vead
Vale tuvastamine
Kokku vale signaalide arv
1
0
1000
0
1000
1
1
4
999
1003
1
2
â0
1997
1997
1
4
â0
3990
3990
10
0
9955
0
9955
10
1
39
990
1029
10
2
â0
1979
1979
10
4
â0
3954
3954
1000
0
632305
0
632305
1000
1
2470
368
2838
1000
2
10
735
745
1000
4
â0
1469
1469
Paistab, et kĂ”ik on lihtne â vali kaitstud andmete pikkuse pĂ”hjal kontrollsumma pikkus miinimumiga vale signaalide arvuga â ja asi on korras.
Siiski, lĂŒhikeste kontrollsummadega on probleem: kuigi need tuvastavad hĂ€sti ĂŒksikuid bitti vigu, vĂ”ivad nad piisavalt suure tĂ”enĂ€osusega lugeda tĂ€iesti juhuslikke andmeid Ă”igeks. HabrĂ©s on juba olnud artikkel, mis kirjeldab .
SeetĂ”ttu, et muuta kontrollsummade juhusliku kokkulangemise praktiline vĂ”imalus peaaegu vĂ”imatuks, tuleb kasutada vĂ€hemalt 32-bitiseid kontrollsummasid (ĂŒle 64 bitise pikkuse puhul kasutatakse tavaliselt krĂŒptograafilisi rĂ€sifunktsioone).
Kuigi ma olen varem kirjutanud, et tuleb iga hinna eest ruumi kokku hoida, kasutame siiski 32-bitist kontrollsummat (16 bitti on liiga vĂ€he, kokkupĂ”rgete tĂ”enĂ€osus on ĂŒle 0,01%; 24 bitti on, nagu öeldakse, ei siia ega sinna).
Siin vĂ”ib tekkida vastuvĂ€ide: kas me tĂ”esti sÀÀstsime iga baidi kompressimise valimisel, et nĂŒĂŒd anda kohe 4 baiti? Ei ole ju parem mitte tihendada ja mitte kontrollsummat lisada? Loomulikult ei, tihendamise puudumine ei tĂ€henda, et me ei vaja terviklikkuse kontrolli.
PolĂŒnoomi valimise osas ei hakka me ratast leiutama, vaid vĂ”tame populaarsed CRC-32C.
See kode tuvastab 6 bittviga pakettides, mille suurus on kuni 22 baiti (ilmselt kÔige levinum juhtum meie jaoks), 4 bittviga pakettides, mille suurus on kuni 655 baiti (ka levinud juhtum meie jaoks), 2 vÔi mis tahes paaritu arv bittvigu pakettides, mille suurus on mÔistlik.
Kui kedagi huvitavad ĂŒksikasjad
CRC kohta.
. Tundub, et â ilmselt peamine ekspert CRC alal maailmas.
Uues on , mis tagab veidi paremad parameetrid meie jaoks asjakohaste pakettide pikkuste jaoks, kuid ma ei pidanud vahet oluliseks ja olen piisavalt kompetentne, et valida kohandatud kood standardse ja pÔhjalikult uuritud koodi asemel.
Veelgi enam, kuna meil on andmed kokku surutud, on kĂŒsimus: kas arvestada kontrollsummat surutud vĂ”i mitte surutud andmetest?
Argumente surutud andmete kontrollsummana:
- meil on lĂ”puks vaja kontrollida andmete sĂ€ilitamise ohutust â nii et me kontrollime seda otse (samuti kontrollitakse vĂ”imalikud vigadega seotud puudujÀÀgid kompressiooni/dekompressiooni rakendamises, mĂ€lurikked jms);
- algoritm deflate zlibis on piisavalt kĂŒps ja ei tohiks kukutada «kruvitud» sisendandmete korral, pealegi on ta sageli vĂ”imeline iseseisvalt tuvastama vigu sisendvoos, vĂ€hendades seelĂ€bi ĂŒldist tĂ”enĂ€osust, et viga jÀÀb tuvastamata (tegin testi, kus inversioon lĂŒhelt bitilt, zlib tuvastas vea ligikaudu kolmandal juhul).
Argumente surutud andmete kontrollsummana:
- CRC on just loodud vĂ€heste bittvigade jaoks, mis iseloomustavad flash-mĂ€lu (bittviga surutud voos vĂ”ib pĂ”hjustada massilist muudatust vĂ€ljundvoos, kus me teoreetiliselt vĂ”ime «pĂŒĂŒda» kolisiooni);
- mulle ei meeldi idee edastada dekompressorile potentsiaalselt vigaseid andmeid, , kuidas ta reageerib.
Selles projektis otsustasin kÔrvale kalduda tavapÀrasest praktikast, kus sÀilitada kontrollsummat mitte surutud andmetest.
KokkuvÔte: kasutame CRC-32C, kontrollsummat arvutame andmetelt sellisel kujul, nagu need salvestatakse flash-mÀllu (pÀrast surumist).
Ăksus
Liigne ĂŒleliigse kodeerimise kasutamine ei vĂ”imalda andmete kaotust tĂ€ielikult vĂ€listada, kuid see vĂ”ib oluliselt (tihti kordades) vĂ€hendada taastamatute andmete kaotuse tĂ”enĂ€osust.
Saame kasutada erinevaid ĂŒlearuse, et vigu korrigeerida.
Hamming'i koodid suudavad parandada ĂŒksikute bittide vigu, Reed-Salomon koodid on sĂŒmboolsed, mitmed andmekopeerimised koos kontrollsummadega vĂ”i duplikaatkodeerimine nagu RAID-6 vĂ”ivad aidata andmeid taastada isegi massiliste riketega.
Alguses olin ma seadnud eesmÀrgiks laialdase vigadevastase kodeerimise kasutamise, kuid hiljem mÔistsin, et esmalt tuleb teada, millistest vigadest me soovime end kaitsta, ja alles seejÀrel valida kodeerimine.
RÀÀkisime varem, et vigu tuleb tuvastada vÔimalikult kiiresti. Millistes olukordades me vÔime vigadega kokku puutuda?
- Katkestatud salvestamine (mingil pÔhjusel, nÀiteks toitekatkestuse, Raspberry riistvara hangumise tÔttu jne)
Kahjuks jÀÀb sellise vea korral ainus vĂ”imalus, et ignoreerida kehtetuks osutunud salvestusi ja pidada andmeid kadunuks; - Salvestamise vead (mingil pĂ”hjusel kirjutati flash-mĂ€ ĐżĐ°ĐŒŃŃĐž midagi muud, kui oli plaanitud)
Selliseid vigu saame koheselt tuvastada, kui teeme pÀrast salvestamist kontrollimist. - Andmete moonutamine mÀlus salvestamise kÀigus;
- Lugemisvead
Vea parandamiseks piisab, kui kontrollsumma mittesobivuse korral lugemist mitu korda korrata.
Seega ei saa ainult kolmanda tĂŒĂŒbi vead (andmete iseeneslik riknemine salvestamise ajal) parandada ilma vigadevastase kodeerimiseta. Tall on usutav, et sellised vead on siiski ÀÀrmiselt ebatĂ”enĂ€olsed.
KokkuvĂ”te: otsustasime loobuda ĂŒleliigsest kodeerimisest, kuid kui kasutamine nĂ€itab, et see otsus on vale, siis naaseme kĂŒsimuse juurde (koos akumuleeritud statistika riketest, mis vĂ”imaldab valida optimaalse kodeerimistĂŒĂŒbi).
Muud
Loomulikult ei vĂ”imalda artikli formaat iga detaili selgitada (ja minu energia on juba ammendunud), seega lĂ€hen lĂŒhidalt ĂŒle mĂ”ningate punktide, mida varem ei puudutatud.
- Otsustati, et kÔik lehed on "vÔrdvÀÀrsed"
See, there won't be any special pages with metadata, separate streams, etc., instead, there will be a single stream that rewrites all pages sequentially.
This ensures even wear of the pages, eliminates a single point of failure, and it's simply preferable; - It is essential to provide for versioning of the format.
A format without a version number in the header is evil!
It's enough to add a field with some Magic Number (signature) in the page header that indicates the version of the format being used. (I don't think there will be even a dozen of them in practice); - Use a variable-length header for records (of which there are many), aiming to make it 1 byte in length for most cases;
- To encode the length of the header and the length of the trimmed part of the compressed record, use variable-length binary codes.
Was very helpful of Huffman codes. It took just a few minutes to find the necessary variable-length codes.
Data storage format description
Byte order
Fields larger than one byte are stored in big-endian format (network byte order), meaning that 0x1234 is written as 0x12, 0x34.
Paging
All flash memory is divided into equal-sized pages.
The default page size is 32KB, but no more than 1/4 of the total size of the memory chip (for a 4MB chip, there are 128 pages).
Each page stores data independently of the others (meaning the data of one page does not reference the data of another page).
All pages are numbered in natural order (in ascending address order), starting from number 0 (the zero page starts at address 0, the first at 32KB, the second at 64KB, etc.).
The memory chip is used as a circular buffer (ring buffer), meaning that the writing first goes to page number 0, then to number 1, âŠ, when we fill the last page, a new cycle begins and writing continues from the zero page.
Inside the page

At the beginning of the page is a 4-byte page header, followed by a header checksum (CRC-32C), and then records are stored in the format 'header, data, checksum'.
The page header (shown in dark green in the diagram) consists of:
- a two-byte Magic Number field (which is also the version format indicator)
for the current version of the format, it is considered as0xed00 â lehe number; - kahebaidise loendur «Lehe versioon» (mĂ€luringi kirjutamise number).
Lehe salvestused salvestatakse kokku surutud kujul (kasutatakse deflate algoritmi). KĂ”ik salvestused ĂŒhel lehel surutakse ĂŒhes voos (kasutatakse ĂŒhise sĂ”naraamatuga), igal uuel lehel hakkab surumine uuesti. Seega vajab iga salvestuse dekompressioon kĂ”iki eelnevaid salvestusi sellelt lehelt (ja ainult sellest).
Iga salvestus surutakse Z_SYNC_FLUSH mĂ€rgiga, samas on kokku surutud voolu lĂ”pus 4 baiti 0x00, 0x00, 0xff, 0xff, ees vĂ”ib olla ĂŒks vĂ”i kaks nullbaiti.
Seda jÀrjestust (pikkusega 4, 5 vÔi 6 baiti) me ei salvesta vÀlkpöördes.
Salvestuse pealkiri koosneb 1, 2 vÔi 3 baitist, mis sisaldavad:
- ĂŒhe bitti (T), mis tĂ€histab salvestuse tĂŒĂŒpi: 0 â kontekst, 1 â pĂ€evik;
- muutuva pikkusega vĂ€lja (S) 1 kuni 7 bitti, mis mÀÀrab pealkirja pikkuse ja âsabagaâ, mis tuleb lisada salvestusele dekompressiooniks;
- salvestuse pikkuse (L).
VÀÀrtuste tabel S:
S
Pealkirja pikkus, bait
Salvestamisel eemaldatav, bait
0
1
5 (00 00 00 ff ff)
10
1
6 (00 00 00 00 ff ff)
110
2
4 (00 00 ff ff)
1110
2
5 (00 00 00 ff ff)
11110
2
6 (00 00 00 00 ff ff)
1111100
3
4 (00 00 ff ff)
1111101
3
5 (00 00 00 ff ff)
1111110
3
6 (00 00 00 00 ff ff)
Proovisin illustreerida, ei tea, kui selgelt see Ônnestus:

Kollane esindab siin vĂ€lja T, valge â vĂ€lja S, roheline L (surutud andmete pikkus baitides), sinine â surutud andmed, punane â surutud andmete lĂ”ppbaitid, mida ei kirjutata vĂ€lkpöördesse.
Seega saame kĂ”ige levinuma pikkusega salvestuste pealkirjad (kuni 63+5 baiti surutud kujul) salvestada ĂŒhe baitiga.
Iga salvestuse jÀrel on kontrollsummana CRC-32C, mille algvÀÀrtused (init) vÔetakse eelneva kontrollsummat vÀÀrtusest.
CRC-l on âjĂ€tkusuutlikkuseâ omadus, see toimib (pluss-minus bittide pööramine protsessis) sellise valemiga:
.
Seega arvutame tegelikult CRC kÔigist eelnevatest baitidest pealkirjades ja andmetes sellel lehel.
Otseselt kontrollsummale jÀrgneb jÀrgmise salvestuse pealkiri.
Pealkiri on konstrueeritud nii, et selle esimene bait oleks alati erinev 0x00 ja 0xff (kui pealkirja esimesele baitile satume 0xff, siis tÀhendab see, et see on kasutamata ala; 0x00 aga signaalib tÔrget).
Umbes algoritmid
Lugemine vÀlkpöördest
Iga lugemine toimub kontrollsummade kontrollimisega.
Kui kontrollsumma ei kattu, loetakse andmeid mitu korda lootes Ôiged andmed siiski lugeda.
(see on mÔttekas, Linux ei vaheta NOR Flash'i lugemist, on kontrollitud)
Salvestamine mÀlukiipidesse
Salvestame andmed.
Lugeme need.
Kui loetud andmed ei lange kokku salvestatud andmetega, tÀidame ala nullidega ja anname teada veast.
Uue kiibi tööks ettevalmistamine
Initsialiseerimiseks salvestatakse esimesse (tÀpsemalt nullsesse) lehte versiooniga 1 pealkiri.
PĂ€rast seda salvestatakse sellele lehele algne kontekst (sisaldab automaadi UUID ja vaikeseaded).
KÔik, mÀlukiip on töötamiseks valmis.
Automaatide laadimine
Laadimise kÀigus loetakse iga lehe esimesed 8 baiti (pealkiri + CRC), lehti, millel on tundmatu Magic Number vÔi vale CRC, ignoreeritakse.
Ăigeid lehti valitakse maksimaalse versiooniga lehtede seast, neist vĂ”etakse leht, millel on kĂ”rgeim number.
Loetakse esimene kirje, kontrollitakse CRC Ôigsust, konteksti lipu olemasolu. Kui kÔik on korras, loetakse see leht jooksvaks. Kui ei, siis tagastame eelnevale lehele, kuni leiame 'elava' lehe.
Ja leitud lehelt loeme kÔik kirjed, neid, millel on konteksti lipp, rakendame.
Salvestame zlib sÔnastiku (see on vajalik selle lehe tÀiendamiseks).
KÔik, laadimine on lÔpule viidud, kontekst on taastatud, saab töötada.
Kande lisamine pÀevikusse
Kandme surumise Ôige sÔnastiku abil, kasutades Z_SYNC_FLUSH. Kontrollime, kas surutud kande mahub praegusele lehele.
Kui ei mahuta (vÔi lehel oli CRC vigu) - alustame uut lehte (vt alla).
Salvestame kande ja CRC. Kui esines viga - alustame uut lehte.
Uus leht
Valime vaba lehe, millel on madalaim number (vabana peame lehte, millel on vale kontrollsumma pealkirjas vÔi mille versioon on vÀiksem kui praegune). Kui selliseid lehti ei ole, valime lehe, millel on minimaalne number ja mille versioon on vÔrdne praegusega.
Teeme valitud lehe erase. Kontrollime sisu 0xff vastu. Kui midagi on valesti - vÔtame jÀrgmise vaba lehe jne.
Kustutatud lehele salvestame pealkirja, esimese kirjana praeguse konteksti oleku, jÀrgmise - kirjutamata pÀevikukande (kui see on olemas).
Formaadi kohaldatavus
Minu arvates on see hea formaat mis tahes mÔÔdukaid andmeid (lihtne tekst, JSON, MessagePack, CBOR, vÔib-olla protobuf) salvestamiseks NOR Flashis.
Muidugi, formaat on 'kohandatud' SLC NOR Flash jaoks.
Seda ei tohiks kasutada seadmetega, millel on kĂ”rge BER, nagu NAND vĂ”i MLC NOR. (Kas sellist mĂ€lu on ĂŒldse mĂŒĂŒgis? Olen kohanud viiteid ainult vigade parandamise teadusartiklites.).
Veelgi enam, seda ei tohiks kasutada seadmetega, millel on oma FTL: USB flash, SD, MicroSD jne. (Sellele mĂ€lule olen ma teinud formaadi, mille lehekĂŒlje suurus on 512 baiti, allkirjaga iga lehe alguses ja unikaalsete kirje numbritega â mĂ”nikord Ă”nnestus 'glitch' flashilt lihtsa jĂ€rjestikuse lugemisega kĂ”ik andmed taastada.).
SĂ”ltuvalt ĂŒlesannetest saab formaati kasutada ilma muutmisteta mĂ€lupulkadel vahemikus 128Kbit (16Kb) kuni 1Gbit (128Mb). Soovi korral saab seda kasutada ka suurema mahutavusega kiipidel, kuid tĂ”enĂ€oliselt tuleb lehekĂŒlje suurust kohandada. (Aga siin kerkib juba esile majanduslik otstarbekus, NOR Flashi suur mahutus ei ole odav.).
Kui kellelegi tundus formaat huvitav ja ta tahab seda kasutada avatud projektis â kirjutage, pĂŒĂŒan leida aega koodi korrigeerimiseks ja ĂŒles laadimiseks Githubi.
KokkuvÔte
Kuidas nÀha, osutus formaat lÔpuks lihtsaks. ja lausa igavaks..
Artikli formaadis on raske kajastada oma seisukoha arengut, kuid uskuge mind: alguses tahtsin luua midagi keerukat ja purunematut, mis suudaks ellu jÀÀda isegi tuumapommi lÀheduses. Kuid mÔistus (loodetavasti) siiski vÔitis ja prioriteedid nihkusid jÀrk-jÀrgult lihtsuse ja kompaktsetuse suunas.
Kas on vÔimalik, et ma eksisin? Jah, loomulikult. TÀiesti vÔimalik on nÀiteks, et oleme ostnud partiid halva kvaliteediga kiipe. VÔi mÔnel muul pÔhjusel ei vasta seade ootustele usaldusvÀÀrsuse osas.
Kas mul on selle juhtumi jaoks plaan? Ma arvan, et artikkel lugedes ei kahtle te, et plaan on. Ja isegi mitte ĂŒks.
Kui rÀÀkida veidi tÔsisemalt, siis formaat on vÀlja töötatud samaaegselt nii tööversioonina kui ka 'katseÔhku laskmiseks'.
Hetkel töötab kÔik laua peal normaalselt, mÔni pÀev tagasi vÔetakse lahendus kasutusele. (umbes) saja saja seadme, vaatame, mis juhtub "lahing" kasutuses (loodetavasti vÔimaldab formaat usaldusvÀÀrselt vigu tuvastada; nii et saab koguda tÀielikku statistikat). Paari kuu pÀrast saab jÀreldusi teha. (ja kui Ônne pole, siis ka varem).
Kui kasutamise tulemused paljastavad tÔsiseid probleeme ja tÀiendavaid arendusi on vaja, siis kirjutan sellest kindlasti.
Kirjandus
Ei tahtnud koostada pikka igavat nimekirja kasutatud töödest, lÔpuks on Google kÔigile olemas.
Siin otsustasin jĂ€tta loetelu leidudest, mis tundusid mulle eriti huvitavad, kuid jĂ€rk-jĂ€rgult said need otse artikli tekstis, ja nimekirjas jĂ€i alles ainult ĂŒks punkt:
- Utiliit autor zlib. Suudab selgelt kuvada deflate/zlib/gzip arhiivide sisu. Kui pead arvestama deflate (vĂ”i gzip) formaadi sisemise struktuuriga â soovitan soojalt.
Allikas: habr.com
