Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash

pravek

K dispozícii sú predajné automaty vlastnej konštrukcie. Vo vnútri Raspberry Pi a nejaká kabeláž na samostatnej doske. Zapojený je mincovník, akceptor bankoviek, bankový terminál... Všetko je riadené vlastnoručne napísaným programom. Celá história práce sa zapisuje do protokolu na flash disku (MicroSD), ktorý sa potom prenáša cez internet (pomocou USB modemu) na server, kde sa ukladá do databázy. Do 1c sa načítavajú informácie o predaji, nechýba ani jednoduché webové rozhranie na sledovanie atď.

To znamená, že denník je životne dôležitý - pre účtovníctvo (výnosy, tržby atď.), monitorovanie (všetky druhy porúch a iných okolností vyššej moci); Dalo by sa povedať, že toto sú všetky informácie, ktoré o tomto stroji máme.

problém

Flash disky sa ukazujú ako veľmi nespoľahlivé zariadenia. Zlyhávajú so závideniahodnou pravidelnosťou. To vedie k prestojom stroja a (ak z nejakého dôvodu nebolo možné protokol preniesť online) k strate údajov.

Nie je to prvá skúsenosť s používaním flash diskov, predtým tu bol iný projekt s viac ako stovkou zariadení, kde bol zásobník uložený na USB flash diskoch, problémy boli aj so spoľahlivosťou, občas sa objavilo množstvo tých, ktoré zlyhali v r. mesiac bol v desiatkach. Vyskúšali sme rôzne flash disky vrátane značkových s pamäťou SLC a niektoré modely sú spoľahlivejšie ako iné, ale výmena flash diskov problém radikálne nevyriešila.

Varovanie! Longread! Ak vás nezaujíma „prečo“, ale iba „ako“, môžete ísť rovno Na koniec článok.

rozhodnutie

Prvá vec, ktorá vás napadne, je: opustiť MicroSD, nainštalovať napríklad SSD a spustiť z neho. Teoreticky možné, pravdepodobne, ale relatívne drahé a nie také spoľahlivé (je pridaný adaptér USB-SATA; štatistika zlyhania pre lacné SSD tiež nie je povzbudivá).

USB HDD tiež nevyzerá ako obzvlášť atraktívne riešenie.

Preto sme dospeli k tejto možnosti: ponechať bootovanie z MicroSD, ale použiť ich v režime iba na čítanie a uložiť prevádzkový denník (a ďalšie informácie jedinečné pre konkrétny hardvér - sériové číslo, kalibrácie snímačov atď.) niekde inde. .

Téma read-only FS pre maliny je už preštudovaná zvnútra aj zvonku, nebudem sa v tomto článku zdržiavať detailmi implementácie (ale ak bude záujem, možno napíšem miničlánok na túto tému). Jediný bod, ktorý by som rád poznamenal, je, že tak z osobných skúseností, ako aj z recenzií tých, ktorí to už implementovali, dochádza k zvýšeniu spoľahlivosti. Áno, nie je možné úplne zbaviť porúch, ale je celkom možné výrazne znížiť ich frekvenciu. A karty sa zjednocujú, čo obslužnému personálu výrazne uľahčuje ich výmenu.

hardvérová časť

O voľbe typu pamäte nebolo zvlášť pochýb – NOR Flash.
argumenty:

  • jednoduché pripojenie (najčastejšie zbernica SPI, s ktorou už máte skúsenosti, takže sa nepredpokladajú žiadne problémy s hardvérom);
  • smiešna cena;
  • štandardný operačný protokol (implementácia je už v linuxovom jadre, ak si želáte, môžete si vziať tretí, ktorý je tiež prítomný, alebo dokonca napísať svoj vlastný, našťastie je všetko jednoduché);
  • spoľahlivosť a zdroje:
    z typického údajového listu: údaje sa uchovávajú 20 rokov, 100000 XNUMX cyklov vymazania pre každý blok;
    zo zdrojov tretích strán: extrémne nízka BER, predpokladá, že nie sú potrebné kódy na opravu chýb (niektoré práce považujú ECC za NOR, ale zvyčajne stále znamenajú MLC NOR; to sa tiež stáva).

Odhadnime požiadavky na objem a zdroj.

Chcel by som, aby bola zaručená záloha údajov na niekoľko dní. Je to potrebné, aby sa v prípade akýchkoľvek komunikačných problémov nestratila história predaja. Počas tohto obdobia sa zameriame na 5 dní (aj s prihliadnutím na víkendy a sviatky) problém sa dá vyriešiť.

Momentálne zbierame okolo 100kb logov denne (3-4 tisíc záznamov), no postupne toto číslo narastá – pribúda detailov, pribúdajú nové udalosti. Navyše niekedy dochádza k prasknutiu (napríklad niektorý snímač začne spamovať falošne pozitívne výsledky). Vypočítame pre 10 tisíc záznamov 100 bajtov každý - megabajtov za deň.

Celkovo vyjde 5 MB čistých (dobre komprimovaných) dát. Viac k nim (hrubý odhad) 1 MB servisných údajov.

To znamená, že potrebujeme 8 MB čip, ak nepoužívame kompresiu, alebo 4 MB, ak ju používame. Celkom reálne čísla na tento typ pamäte.

Čo sa týka zdroja: ak plánujeme, že celá pamäť sa bude prepisovať maximálne raz za 5 dní, tak za 10 rokov služby dostaneme menej ako tisíc prepisovacích cyklov.
Pripomínam, že výrobca sľubuje stotisíc.

Trochu o NOR vs NAND

Dnes je, samozrejme, oveľa populárnejšia pamäť NAND, ale na tento projekt by som ju nepoužil: NAND na rozdiel od NOR nevyhnutne vyžaduje použitie kódov na opravu chýb, tabuľku zlých blokov atď., A tiež nohy Čipy NAND zvyčajne oveľa viac.

Nevýhody NOR zahŕňajú:

  • malý objem (a teda vysoká cena za megabajt);
  • nízka rýchlosť komunikácie (hlavne kvôli tomu, že sa používa sériové rozhranie, zvyčajne SPI alebo I2C);
  • pomalé vymazávanie (v závislosti od veľkosti bloku trvá zlomok sekundy až niekoľko sekúnd).

Zdá sa, že pre nás nie je nič kritické, takže pokračujeme.

Ak sú detaily zaujímavé, bol vybraný mikroobvod at25df321a (to je však nepodstatné, na trhu je množstvo analógov, ktoré sú kompatibilné v pinout a príkazovom systéme; aj keď chceme nainštalovať mikroobvod od iného výrobcu a/alebo inej veľkosti, všetko bude fungovať bez zmeny kód).

Používam ovládač zabudovaný v linuxovom jadre, na Raspberry je vďaka podpore prekrytia stromom zariadení všetko veľmi jednoduché – skompilované prekrytie treba vložiť do /boot/overlays a mierne upraviť /boot/config.txt.

Príklad súboru dts

Aby som bol úprimný, nie som si istý, či je to napísané bez chýb, ale funguje to.

/*
 * 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?";
    };
};

A ďalší riadok v súbore config.txt

dtoverlay=at25:spimaxfrequency=50000000

Popis pripojenia čipu k Raspberry Pi vynechám. Na jednej strane nie som odborník na elektroniku, na druhej strane je tu všetko banálne aj pre mňa: mikroobvod má iba 8 nôh, z ktorých potrebujeme uzemnenie, napájanie, SPI (CS, SI, SO, SCK ); úrovne sú rovnaké ako u Raspberry Pi, nie je potrebná žiadna ďalšia kabeláž - stačí pripojiť uvedených 6 kolíkov.

Vyhlásenie o probléme

Ako obvykle, problémové vyhlásenie prechádza niekoľkými iteráciami a zdá sa mi, že je čas na ďalšiu. Zastavme sa teda, dáme dokopy už napísané a ujasnime si detaily, ktoré zostávajú v tieni.

Preto sme sa rozhodli, že protokol bude uložený v SPI NOR Flash.

Čo je NOR Flash pre tých, ktorí nevedia?

Toto je energeticky nezávislá pamäť, s ktorou môžete vykonávať tri operácie:

  1. Čítanie:
    Najbežnejšie čítanie: prenesieme adresu a prečítame toľko bajtov, koľko potrebujeme;
  2. záznam:
    Zápis do NOR flash vyzerá ako bežný, no má jednu zvláštnosť: zmeniť sa dá len 1 na 0, ale nie naopak. Ak by sme napríklad mali v pamäťovej bunke 0x55, tak po zapísaní 0x0f do nej už bude uložených 0x05 (pozri tabuľku nižšie);
  3. Vymazať:
    Samozrejme, musíme vedieť urobiť opačnú operáciu – zmeniť 0 na 1, presne na to slúži operácia vymazania. Na rozdiel od prvých dvoch nepracuje s bajtmi, ale s blokmi (minimálny mazací blok vo vybranom čipe je 4 kb). Vymazanie zničí celý blok a je to jediný spôsob, ako zmeniť 0 na 1. Preto pri práci s pamäťou flash často musíte zarovnať dátové štruktúry na hranicu bloku vymazania.
    Nahrávanie v NOR Flash:

Binárne dáta

to bolo
01010101

Zaznamenané
00001111

Stal sa
00000101

Samotný protokol predstavuje sekvenciu záznamov rôznej dĺžky. Typická dĺžka záznamu je približne 30 bajtov (hoci sa niekedy vyskytujú záznamy s dĺžkou niekoľko kilobajtov). V tomto prípade s nimi pracujeme jednoducho ako s množinou bajtov, ale ak máte záujem, vo vnútri záznamov sa používa CBOR

Okrem denníka musíme uložiť niektoré informácie o „nastaveniach“, či už aktualizované alebo nie: určité ID zariadenia, kalibrácie senzorov, príznak „zariadenie je dočasne vypnuté“ atď.
Tieto informácie sú množinou záznamov kľúč – hodnota, ktoré sú tiež uložené v CBOR. Týchto informácií nemáme veľa (najviac niekoľko kilobajtov) a aktualizujeme ich len zriedka.
Ďalej to budeme nazývať kontext.

Ak si pamätáme, kde tento článok začal, je veľmi dôležité zabezpečiť spoľahlivé ukladanie dát a podľa možnosti nepretržitú prevádzku aj v prípade zlyhania hardvéru/poškodenia dát.

Aké zdroje problémov možno považovať?

  • Vypnite počas operácií zápisu/vymazania. Toto je z kategórie „neexistuje žiadny trik proti páčidlu“.
    Informácie od diskusie na stackexchange: keď je pri práci s flashom vypnuté napájanie, vymazávanie (nastavené na 1) aj zápis (nastavené na 0) vedú k nedefinovanému správaniu: dáta je možné zapisovať, čiastočne zapisovať (povedzme, preniesli sme 10 bajtov/80 bitov , ale ešte nie je možné zapísať iba 45 bitov), ​​je tiež možné, že niektoré bity budú v „strednom“ stave (čítanie môže produkovať 0 aj 1);
  • Chyby v samotnej flash pamäti.
    BER, aj keď je veľmi nízka, nemôže sa rovnať nule;
  • Chyby zbernice
    Dáta prenášané cez SPI nie sú nijako chránené, môže dochádzať k chybám jednotlivých bitov aj chybám synchronizácie – strata alebo vloženie bitov (čo vedie k masívnemu skresleniu údajov);
  • Iné chyby/chyby
    Chyby v kóde, poruchy Raspberry, zasahovanie mimozemšťanov...

Sformuloval som požiadavky, ktorých splnenie je podľa môjho názoru nevyhnutné na zabezpečenie spoľahlivosti:

  • záznamy musia ísť do flash pamäte okamžite, oneskorené zápisy sa neberú do úvahy; - ak sa vyskytne chyba, musí sa odhaliť a spracovať čo najskôr; - systém sa musí, ak je to možné, zotaviť z chýb.
    (príklad zo života „ako by to nemalo byť“, s ktorým sa už myslím stretol každý: po núdzovom reštarte je súborový systém „rozbitý“ a operačný systém sa nespustí)

Nápady, prístupy, úvahy

Keď som začal premýšľať o tomto probléme, hlavou mi preblesklo veľa nápadov, napr.

  • používať kompresiu údajov;
  • využívať šikovné dátové štruktúry, napríklad ukladať hlavičky záznamov oddelene od samotných záznamov, takže ak sa v niektorom zázname vyskytne chyba, zvyšok si môžete bez problémov prečítať;
  • použite bitové polia na ovládanie dokončenia nahrávania po vypnutí napájania;
  • ukladať kontrolné súčty pre všetko;
  • použite nejaký typ kódovania odolného voči hluku.

Niektoré z týchto myšlienok boli použité, zatiaľ čo iné sa rozhodli opustiť. Poďme pekne po poriadku.

Kompresia údajov

Samotné udalosti, ktoré zaznamenávame v denníku, sú dosť podobné a opakovateľné („hodenie 5 rubľovej mince“, „stlačenie tlačidla na rozdávanie“, ...). Preto by kompresia mala byť dosť účinná.

Kompresná réžia je zanedbateľná (náš procesor je pomerne výkonný, aj prvý Pi mal jedno jadro s frekvenciou 700 MHz, súčasné modely majú niekoľko jadier s frekvenciou nad gigahertz), výmenný kurz s úložiskom je nízky (niekoľko megabajtov za sekundu), veľkosť záznamov je malá. Vo všeobecnosti, ak má kompresia vplyv na výkon, bude to len pozitívne. (absolútne nekritické, len konštatujem). Navyše nemáme skutočný embedded, ale bežný Linux - implementácia by teda nemala vyžadovať veľa úsilia (stačí len prepojiť knižnicu a použiť z nej niekoľko funkcií).

Kúsok protokolu bol prevzatý z fungujúceho zariadenia (1.7 MB, 70 4 záznamov) a najprv skontrolovaný na komprimovateľnosť pomocou gzip, lz2, lzop, bzipXNUMX, xz, zstd dostupných v počítači.

  • gzip, xz, zstd vykazovali podobné výsledky (40 kB).
    Prekvapilo ma, že módne xz sa tu prejavilo na úrovni gzip alebo zstd;
  • lzip s predvolenými nastaveniami dával o niečo horšie výsledky;
  • lz4 a lzop ukázali nie veľmi dobré výsledky (150 Kb);
  • bzip2 ukázal prekvapivo dobrý výsledok (18 Kb).

Dáta sú teda veľmi dobre komprimované.
Takže (ak nenájdeme fatálne chyby) bude kompresia! Jednoducho preto, že na jeden flash disk sa zmestí viac dát.

Zamyslime sa nad nevýhodami.

Prvý problém: už sme sa dohodli, že každý záznam musí ísť okamžite na flash. Archivátor zvyčajne zhromažďuje údaje zo vstupného toku, kým sa nerozhodne, že je čas na zápis cez víkend. Musíme okamžite prijať komprimovaný blok údajov a uložiť ho do energeticky nezávislej pamäte.

Vidím tri spôsoby:

  1. Komprimujte každý záznam pomocou slovníkovej kompresie namiesto vyššie uvedených algoritmov.
    Je to úplne funkčná možnosť, ale nepáči sa mi to. Aby sa zabezpečila viac či menej slušná úroveň kompresie, musí byť slovník „šitý na mieru“ konkrétnym údajom, akákoľvek zmena povedie ku katastrofálnemu poklesu úrovne kompresie. Áno, problém možno vyriešiť vytvorením novej verzie slovníka, ale toto je bolesť hlavy - budeme musieť uložiť všetky verzie slovníka; v každom zázname budeme musieť uviesť, akou verziou slovníka bol komprimovaný...
  2. Komprimujte každý záznam pomocou „klasických“ algoritmov, ale nezávisle od ostatných.
    Uvažované kompresné algoritmy nie sú navrhnuté tak, aby pracovali so záznamami tejto veľkosti (desiatky bajtov), ​​kompresný pomer bude jednoznačne menší ako 1 (to znamená zvýšenie objemu údajov namiesto kompresie);
  3. Vykonajte FLUSH po každom nahrávaní.
    Mnoho kompresných knižníc podporuje FLUSH. Toto je príkaz (alebo parameter kompresnej procedúry), po prijatí ktorého archivátor vytvorí komprimovaný tok, aby ho bolo možné použiť na obnovenie všetko nekomprimované dáta, ktoré už boli prijaté. Taký analóg sync v súborových systémoch resp commit v sql.
    Dôležité je, že následné operácie kompresie budú môcť využívať nahromadený slovník a kompresný pomer neutrpí toľko ako v predchádzajúcej verzii.

Myslím, že je zrejmé, že som si vybral tretiu možnosť, pozrime sa na to podrobnejšie.

Nájdené skvelý článok o FLUSH v zlib.

Urobil som test kolena na základe článku, zobral som 70 60 záznamov denníka zo skutočného zariadenia s veľkosťou stránky XNUMX kb (k veľkosti stránky sa vrátime neskôr) dostal:

Surové údaje
Kompresia gzip -9 (bez FLUSH)
zlib so Z_PARTIAL_FLUSH
zlib so Z_SYNC_FLUSH

Objem, kB
1692
40
352
604

Na prvý pohľad je cena za FLUSH prehnane vysoká, no v skutočnosti nemáme na výber – buď nekomprimovať vôbec, alebo komprimovať (a veľmi efektívne) pomocou FLUSH. Netreba zabúdať, že máme 70 tisíc záznamov, redundancia zavedená Z_PARTIAL_FLUSH je len 4-5 bajtov na záznam. A kompresný pomer vyšiel takmer 5:1, čo je viac než výborný výsledok.

Môže to byť prekvapenie, ale Z_SYNC_FLUSH je v skutočnosti efektívnejší spôsob, ako vykonať FLUSH

Pri použití Z_SYNC_FLUSH budú posledné 4 bajty každej položky vždy 0x00, 0x00, 0xff, 0xff. A ak ich poznáme, nemusíme ich ukladať, takže konečná veľkosť je iba 324 Kb.

Článok, na ktorý som odkazoval, má vysvetlenie:

Pridá sa nový blok typu 0 s prázdnym obsahom.

Blok typu 0 s prázdnym obsahom pozostáva z:

  • hlavička trojbitového bloku;
  • 0 až 7 bitov rovných nule, aby sa dosiahlo zarovnanie bajtov;
  • štvorbajtová sekvencia 00 00 FF FF.

Ako môžete ľahko vidieť, v poslednom bloku pred týmito 4 bajtmi je od 3 do 10 nulových bitov. Prax však ukázala, že v skutočnosti existuje najmenej 10 nulových bitov.

Ukazuje sa, že takéto krátke bloky údajov sú zvyčajne (vždy?) kódované pomocou bloku typu 1 (pevný blok), ktorý nevyhnutne končí 7 nulovými bitmi, čo dáva celkovo 10-17 garantovaných nulových bitov (a zvyšok bude byť nula s pravdepodobnosťou asi 50%).

Takže na testovacích údajoch je v 100% prípadov jeden nulový bajt pred 0x00, 0x00, 0xff, 0xff a vo viac ako tretine prípadov sú dva nulové bajty (možno faktom je, že používam binárny CBOR a pri textovom JSON by boli bežnejšie bloky typu 2 - dynamický blok, respektíve bloky bez ďalších nulových bajtov pred 0x00, 0x00, 0xff, 0xff).

Celkovo je možné pomocou dostupných testovacích údajov zmestiť menej ako 250 Kb komprimovaných údajov.

Trochu viac môžete ušetriť žonglovaním s bitmi: zatiaľ ignorujeme prítomnosť niekoľkých nulových bitov na konci bloku, niekoľko bitov na začiatku bloku sa tiež nemení...
Potom som sa však rázne rozhodol prestať, inak by som týmto tempom mohol skončiť vývojom vlastného archivátora.

Celkovo z mojich testovacích údajov som dostal 3-4 bajty na zápis, kompresný pomer sa ukázal byť viac ako 6:1. Budem úprimný: takýto výsledok som nečakal, podľa môjho názoru čokoľvek lepšie ako 2:1 je už výsledok, ktorý ospravedlňuje použitie kompresie.

Všetko je v poriadku, ale zlib (deflate) je stále archaický, zaslúžený a trochu staromódny kompresný algoritmus. Už len fakt, že posledných 32 Kb nekomprimovaného dátového toku sa používa ako slovník, dnes vyzerá zvláštne (to znamená, že ak je nejaký dátový blok veľmi podobný tomu, čo bol vo vstupnom toku pred 40 Kb, potom sa začne znova archivovať, a nebude odkazovať na predchádzajúci výskyt). V módnych moderných archivátoroch sa veľkosť slovníka často meria skôr v megabajtoch ako v kilobajtoch.

Pokračujeme teda v našej miništúdii archivárov.

Ďalej sme testovali bzip2 (nezabudnite, že bez FLUSH ukazoval fantastický kompresný pomer takmer 100:1). Bohužiaľ to fungovalo veľmi zle s FLUSH; veľkosť komprimovaných údajov sa ukázala byť väčšia ako nekomprimovaných údajov.

Moje predpoklady o príčinách zlyhania

Libbz2 ponúka iba jednu možnosť vyprázdnenia, ktorá zrejme vyčistí slovník (podobne ako Z_FULL_FLUSH v zlib); potom sa nehovorí o žiadnej efektívnej kompresii.

A posledný testovaný bol zstd. V závislosti od parametrov sa komprimuje buď na úrovni gzip, ale oveľa rýchlejšie, alebo lepšie ako gzip.

Bohužiaľ, s FLUSH to nefungovalo veľmi dobre: ​​veľkosť komprimovaných údajov bola asi 700 kb.

Я položil otázku na stránke github projektu som dostal odpoveď, že by ste mali počítať až s 10 bajtmi servisných údajov pre každý blok komprimovaných údajov, čo je blízko k získaným výsledkom; neexistuje spôsob, ako dohnať defláciu.

V tomto bode som sa rozhodol zastaviť svoje experimenty s archivátormi (pripomínam, že xz, lzip, lzo, lz4 sa neukázali ani v testovacej fáze bez FLUSH a o exotickejších kompresných algoritmoch som neuvažoval).

Vráťme sa k problémom s archiváciou.

Druhým (ako sa hovorí v poradí, nie hodnotovo) problémom je, že komprimované dáta sú jedným tokom, v ktorom sú neustále odkazy na predchádzajúce časti. Ak sa teda poškodí časť komprimovaných údajov, prídeme nielen o súvisiaci blok nekomprimovaných údajov, ale aj o všetky nasledujúce.

Existuje prístup na vyriešenie tohto problému:

  1. Zabráňte výskytu problému - pridajte ku komprimovaným údajom redundanciu, ktorá vám umožní identifikovať a opraviť chyby; o tom si povieme neskôr;
  2. Minimalizujte následky, ak sa vyskytne problém
    Už sme povedali, že môžete komprimovať každý blok údajov nezávisle a problém zmizne sám o sebe (poškodenie údajov jedného bloku povedie k strate údajov iba pre tento blok). Ide však o extrémny prípad, kedy bude kompresia dát neúčinná. Opačný extrém: použiť všetky 4 MB nášho čipu ako jeden archív, čo nám poskytne vynikajúcu kompresiu, ale katastrofálne následky v prípade poškodenia dát.
    Áno, z hľadiska spoľahlivosti je potrebný kompromis. Musíme si však uvedomiť, že vyvíjame formát na ukladanie údajov pre energeticky nezávislú pamäť s extrémne nízkou BER a deklarovanou dobou uchovávania údajov 20 rokov.

Počas experimentov som zistil, že viac či menej badateľné straty na úrovni kompresie začínajú na blokoch komprimovaných dát s veľkosťou menšou ako 10 KB.
Už bolo spomenuté, že používaná pamäť je stránkovaná; nevidím dôvod, prečo by sa nemala používať korešpondencia „jedna stránka - jeden blok komprimovaných údajov“.

To znamená, že minimálna primeraná veľkosť stránky je 16 kB (s rezervou pre servisné informácie). Takáto malá veľkosť stránky však výrazne obmedzuje maximálnu veľkosť záznamu.

Aj keď zatiaľ neočakávam záznamy väčšie ako niekoľko kilobajtov v komprimovanej forme, rozhodol som sa použiť 32Kb stránky (celkovo 128 stránok na čip).

Zhrnutie:

  • Údaje ukladáme komprimované pomocou zlib (deflate);
  • Pre každý záznam nastavíme Z_SYNC_FLUSH;
  • Pre každý komprimovaný záznam orezávame koncové bajty (napr. 0x00, 0x00, 0xff, 0xff); v hlavičke uvádzame, koľko bajtov sme odrezali;
  • Údaje ukladáme na stránkach s veľkosťou 32 kB; vnútri stránky je jediný prúd komprimovaných údajov; Na každej stránke začíname s kompresiou znova.

A pred dokončením kompresie by som vás chcel upozorniť na skutočnosť, že máme len niekoľko bajtov komprimovaných údajov na záznam, takže je mimoriadne dôležité nenafúknuť informácie o službe, tu sa počíta každý bajt.

Ukladanie hlavičiek údajov

Keďže máme záznamy s premenlivou dĺžkou, musíme nejako určiť umiestnenie/hranice záznamov.

Poznám tri prístupy:

  1. Všetky záznamy sú uložené v nepretržitom prúde, najprv je hlavička záznamu obsahujúca dĺžku a potom samotný záznam.
    V tomto uskutočnení môžu mať hlavičky aj dáta premenlivú dĺžku.
    V podstate dostaneme jednotlivo prepojený zoznam, ktorý sa používa stále;
  2. Hlavičky a samotné záznamy sú uložené v samostatných prúdoch.
    Použitím hlavičiek konštantnej dĺžky zaisťujeme, že poškodenie jednej hlavičky neovplyvní ostatné.
    Podobný prístup sa používa napríklad v mnohých súborových systémoch;
  3. Záznamy sú uložené v súvislom toku, hranica záznamu je určená určitým markerom (znak/sekvencia znakov, ktorá je v rámci dátových blokov zakázaná). Ak je vo vnútri záznamu značka, potom ju nahradíme nejakou sekvenciou (escapujeme ju).
    Podobný prístup sa používa napríklad v protokole PPP.

Budem ilustrovať.

Možnosť 1:
Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash
Všetko je tu veľmi jednoduché: ak poznáme dĺžku záznamu, môžeme vypočítať adresu ďalšej hlavičky. Pohybujeme sa teda po nadpisoch, kým nenarazíme na oblasť vyplnenú 0xff (voľná oblasť) alebo na koniec stránky.

Možnosť 2:
Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash
Vzhľadom na variabilnú dĺžku záznamu nevieme vopred povedať, koľko záznamov (a teda hlavičiek) budeme potrebovať na stránku. Hlavičky a samotné údaje môžete rozložiť na rôzne stránky, ja však preferujem iný prístup: hlavičky aj údaje umiestnime na jednu stránku, ale hlavičky (konštantnej veľkosti) pochádzajú zo začiatku stránky a údaje (premenlivej dĺžky) pochádzajú z konca. Hneď ako sa „stretnú“ (nie je dostatok voľného miesta na nový záznam), považujeme túto stránku za dokončenú.

Možnosť 3:
Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash
Do hlavičky nie je potrebné ukladať dĺžku ani iné informácie o umiestnení údajov, stačia značky označujúce hranice záznamov. Údaje však musia byť spracované pri zápise/čítaní.
Ako značku by som použil 0xff (ktorá po vymazaní vyplní stránku), takže voľná plocha sa určite nebude považovať za dáta.

Porovnávacia tabuľka:

Možnosť 1
Možnosť 2
Možnosť 3

Tolerancia chýb
-
+
+

hustota
+
-
+

Zložitosť implementácie
*
**
**

Možnosť 1 má fatálnu chybu: ak sa poškodí niektorá z hlavičiek, zničí sa celý nasledujúci reťazec. Zvyšné možnosti umožňujú obnoviť niektoré dáta aj v prípade masívneho poškodenia.
Tu je ale vhodné pripomenúť, že údaje sme sa rozhodli ukladať v komprimovanej podobe, a tak po „pokazenom“ zázname stratíme všetky údaje na stránke, takže aj keď je v tabuľke mínus, berte to do úvahy.

Kompaktnosť:

  • v prvej možnosti musíme do hlavičky uložiť iba dĺžku, ak použijeme celé čísla s premenlivou dĺžkou, potom si vo väčšine prípadov vystačíme s jedným bytom;
  • v druhej možnosti musíme uložiť počiatočnú adresu a dĺžku; záznam musí mať konštantnú veľkosť, odhadujem 4 bajty na záznam (dva bajty na posun a dva bajty na dĺžku);
  • tretia možnosť potrebuje iba jeden znak na označenie začiatku nahrávania, plus samotná nahrávka sa vďaka tieneniu zvýši o 1-2%. Vo všeobecnosti približne rovnaká ako prvá možnosť.

Spočiatku som považoval druhú možnosť za hlavnú (a dokonca som napísal implementáciu). Upustil som od toho, až keď som sa konečne rozhodol použiť kompresiu.

Možno niekedy ešte využijem podobnú možnosť. Ak mám napríklad riešiť ukladanie dát pre loď cestujúcu medzi Zemou a Marsom, budú tam úplne iné požiadavky na spoľahlivosť, kozmické žiarenie, ...

Pokiaľ ide o tretiu možnosť: dal som jej dve hviezdičky za náročnosť implementácie jednoducho preto, že sa mi nepáči motať sa okolo tienenia, meniť dĺžku v procese atď. Áno, možno som zaujatý, ale budem musieť napísať kód - prečo sa nútiť robiť niečo, čo sa vám nepáči.

Zhrnutie: Z dôvodu efektívnosti a jednoduchosti implementácie volíme možnosť ukladania vo forme reťazcov „hlavička s dĺžkou - dáta s premenlivou dĺžkou“.

Používanie bitových polí na monitorovanie úspešnosti operácií zápisu

Už si nepamätám, kde ma to napadlo, ale vyzerá to asi takto:
Pre každý záznam alokujeme niekoľko bitov na uloženie príznakov.
Ako sme už povedali, po vymazaní sú všetky bity vyplnené 1s a môžeme zmeniť 1 na 0, ale nie naopak. Takže pre „príznak nie je nastavený“ použijeme 1, pre „príznak je nastavený“ použijeme 0.

Takto môže vloženie záznamu s premenlivou dĺžkou do flashu vyzerať:

  1. Nastavte príznak „nahrávanie dĺžky začalo“;
  2. Zaznamenajte dĺžku;
  3. Nastavte príznak „zaznamenávanie údajov začalo“;
  4. Zaznamenávame údaje;
  5. Nastavte príznak „nahrávanie skončilo“.

Okrem toho budeme mať príznak „vyskytla sa chyba“, čo predstavuje celkovo 4 bitové príznaky.

V tomto prípade máme dva stabilné stavy „1111“ – nahrávanie sa nezačalo a „1000“ – nahrávanie bolo úspešné; v prípade neočakávaného prerušenia nahrávacieho procesu dostaneme medzistavy, ktoré vieme následne odhaliť a spracovať.

Prístup je zaujímavý, no chráni len pred náhlymi výpadkami prúdu a podobnými poruchami, čo je, samozrejme, dôležité, no zďaleka to nie je jediný (či dokonca hlavný) dôvod prípadných porúch.

Zhrnutie: Poďme ďalej hľadať dobré riešenie.

Kontrolné súčty

Kontrolné súčty tiež umožňujú uistiť sa (s primeranou pravdepodobnosťou), že čítame presne to, čo malo byť napísané. A na rozdiel od bitových polí diskutovaných vyššie vždy fungujú.

Ak vezmeme do úvahy zoznam potenciálnych zdrojov problémov, o ktorých sme hovorili vyššie, potom je kontrolný súčet schopný rozpoznať chybu bez ohľadu na jej pôvod (možno s výnimkou zlomyseľných mimozemšťanov - môžu tiež sfalšovať kontrolný súčet).

Ak je teda naším cieľom overiť, či sú údaje neporušené, kontrolné súčty sú skvelý nápad.

Výber algoritmu na výpočet kontrolného súčtu nevyvolal žiadne otázky - CRC. Na jednej strane matematické vlastnosti umožňujú zachytiť určité typy chýb na 100%, na druhej strane na náhodných dátach tento algoritmus zvyčajne ukazuje pravdepodobnosť kolízií nie oveľa väčšiu ako teoretický limit. Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash. Možno to nie je najrýchlejší algoritmus, ani to nie je vždy minimum z hľadiska počtu kolízií, ale má veľmi dôležitú kvalitu: v testoch, s ktorými som sa stretol, sa nenašli žiadne vzory, v ktorých by jednoznačne zlyhal. Hlavnou kvalitou je v tomto prípade stabilita.

Príklad volumetrickej štúdie: Časť 1, Časť 2 (odkazy na narod.ru, prepáčte).

Úloha výberu kontrolného súčtu však nie je úplná, CRC je celá rodina kontrolných súčtov. Musíte sa rozhodnúť pre dĺžku a potom zvoliť polynóm.

Výber dĺžky kontrolného súčtu nie je taká jednoduchá otázka, ako sa na prvý pohľad zdá.

Dovoľte mi ilustrovať:
Majme pravdepodobnosť chyby v každom byte Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash a ideálny kontrolný súčet, vypočítajme priemerný počet chýb na milión záznamov:

Dáta, bajt
Kontrolný súčet, bajt
Nezistené chyby
Zistenie falošných chýb
Celkový počet falošných poplachov

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

Zdalo by sa, že všetko je jednoduché – v závislosti od dĺžky chránených údajov zvoľte dĺžku kontrolného súčtu s minimom nesprávnych kladných hodnôt – a trik je vo vrecku.

Problém však nastáva pri krátkych kontrolných súčtoch: sú síce dobré v detekcii jednotlivých bitových chýb, ale s dosť vysokou pravdepodobnosťou dokážu akceptovať úplne náhodné dáta ako správne. O Habrém už bol článok popisujúci problém v reálnom živote.

Preto, aby bola náhodná zhoda kontrolného súčtu takmer nemožná, musíte použiť kontrolné súčty s dĺžkou 32 bitov alebo viac. (pre dĺžky väčšie ako 64 bitov sa zvyčajne používajú kryptografické hašovacie funkcie).

Napriek tomu, že som už skôr písal, že musíme všetkými prostriedkami šetriť miesto, stále použijeme 32-bitový kontrolný súčet (16 bitov nestačí, pravdepodobnosť kolízie je viac ako 0.01 %; a 24 bitov, keďže povedzme, nie sú ani tu, ani tam).

Tu môže vzniknúť námietka: ušetrili sme každý bajt pri výbere kompresie, aby sme teraz dali 4 bajty naraz? Nebolo by lepšie nekomprimovať ani nepridávať kontrolný súčet? Samozrejme nie, bez kompresie neznamená, že nepotrebujeme kontrolu integrity.

Pri výbere polynómu nebudeme znovu objavovať koleso, ale vezmeme teraz populárny CRC-32C.
Tento kód detekuje 6-bitové chyby na paketoch do 22 bajtov (možno najbežnejší prípad u nás), 4-bitové chyby na paketoch do 655 bajtov (pre nás tiež bežný prípad), 2 alebo akýkoľvek nepárny počet bitových chýb na paketoch akejkoľvek primeranej dĺžky.

Ak by niekoho zaujímali podrobnosti

článok z Wikipédie o CRC.

Parametre kódu crc-32c na Webová stránka Koopman — možno popredný špecialista na CRC na planéte.

В jeho článok je ďalší zaujímavý kód, ktorý poskytuje o niečo lepšie parametre pre dĺžky paketov, ktoré sú pre nás relevantné, ale rozdiel som nepovažoval za významný a bol som dostatočne kompetentný vybrať si vlastný kód namiesto štandardného a dobre preskúmaného.

Keďže sú naše údaje komprimované, vyvstáva otázka: mali by sme vypočítať kontrolný súčet komprimovaných alebo nekomprimovaných údajov?

Argumenty v prospech výpočtu kontrolného súčtu nekomprimovaných údajov:

  • Bezpečnosť ukladania dát musíme v konečnom dôsledku skontrolovať – teda kontrolujeme ju priamo (súčasne sa skontrolujú možné chyby pri implementácii kompresie/dekompresie, poškodenia spôsobené poškodenou pamäťou a pod.);
  • Algoritmus deflate v zlib má pomerne vyspelú implementáciu a by nemal padať s „krivými“ vstupnými dátami, navyše často dokáže samostatne detekovať chyby vo vstupnom toku, čím znižuje celkovú pravdepodobnosť neodhalenia chyby (vykonal test s invertovaním jediného bitu v krátkom zázname, zlib zistil chybu asi v tretine prípadov).

Argumenty proti výpočtu kontrolného súčtu nekomprimovaných údajov:

  • CRC je „šité na mieru“ špeciálne pre niekoľko bitových chýb, ktoré sú charakteristické pre flash pamäť (bitová chyba v komprimovanom toku môže spôsobiť masívnu zmenu vo výstupnom toku, na ktorom čisto teoreticky môžeme „chytiť“ kolíziu);
  • Veľmi sa mi nepáči myšlienka odovzdávania potenciálne poškodených údajov do dekompresora, Kto vieako bude reagovať.

V tomto projekte som sa rozhodol odkloniť od všeobecne akceptovanej praxe ukladania kontrolného súčtu nekomprimovaných dát.

Zhrnutie: Používame CRC-32C, kontrolný súčet vypočítame z údajov vo forme, v akej sú zapísané na flash (po kompresii).

Nadbytok

Použitie redundantného kódovania samozrejme nevylučuje stratu údajov, môže však výrazne (často o mnoho rádov) znížiť pravdepodobnosť nenávratnej straty údajov.

Na opravu chýb môžeme použiť rôzne typy redundancie.
Hammingove kódy dokážu opraviť jednotlivé bitové chyby, Reed-Solomonove kódy znakov, viac kópií údajov v kombinácii s kontrolnými súčtami alebo kódovania ako RAID-6 môžu pomôcť obnoviť údaje aj v prípade masívneho poškodenia.
Spočiatku som sa zaviazal k širokému používaniu kódovania odolného voči chybám, ale potom som si uvedomil, že najprv musíme mať predstavu o tom, pred akými chybami sa chceme chrániť, a potom si vybrať kódovanie.

Už sme povedali, že chyby je potrebné zachytiť čo najrýchlejšie. V ktorých bodoch sa môžeme stretnúť s chybami?

  1. Nedokončené nahrávanie (z nejakého dôvodu bolo v čase nahrávania vypnuté napájanie, Raspberry zamrzol, ...)
    Bohužiaľ, v prípade takejto chyby zostáva len ignorovať neplatné záznamy a považovať dáta za stratené;
  2. Chyby zápisu (z nejakého dôvodu to, čo bolo zapísané do pamäte flash, nebolo to, čo bolo zapísané)
    Takéto chyby môžeme okamžite zistiť, ak vykonáme testovacie čítanie ihneď po zaznamenaní;
  3. Skreslenie údajov v pamäti počas ukladania;
  4. Chyby pri čítaní
    Na opravu, ak sa kontrolný súčet nezhoduje, stačí čítanie niekoľkokrát zopakovať.

To znamená, že iba chyby tretieho typu (spontánne poškodenie údajov počas ukladania) nemožno opraviť bez kódovania odolného voči chybám. Zdá sa, že takéto chyby sú stále veľmi nepravdepodobné.

Zhrnutie: bolo rozhodnuté opustiť redundantné kódovanie, ale ak prevádzka ukáže chybu tohto rozhodnutia, vráťte sa k zváženiu problému (s už nahromadenými štatistikami o zlyhaniach, ktoré vám umožnia vybrať optimálny typ kódovania).

Ostatné

Samozrejme, formát článku nám neumožňuje zdôvodniť každý kúsok vo formáte (a sily mi už došli), takže stručne prejdem k niektorým bodom, ktorých som sa predtým nedotkol.

  • Rozhodlo sa, že všetky stránky budú „rovnaké“
    To znamená, že nebudú existovať žiadne špeciálne stránky s metadátami, oddelené vlákna atď., ale namiesto toho jediné vlákno, ktoré postupne prepíše všetky stránky.
    To zaisťuje rovnomerné opotrebovanie stránok, žiadny jediný bod zlyhania, a to sa mi páči;
  • Je nevyhnutné poskytnúť verziu formátu.
    Formát bez čísla verzie v hlavičke je zlý!
    Do hlavičky stránky stačí pridať pole s určitým magickým číslom (podpis), ktoré bude označovať verziu použitého formátu (Nemyslím si, že v praxi ich bude ani tucet);
  • Použite hlavičku s premenlivou dĺžkou pre záznamy (ktorých je veľa), pričom vo väčšine prípadov sa snažte, aby mala dĺžku 1 bajt;
  • Na zakódovanie dĺžky hlavičky a dĺžky orezanej časti komprimovaného záznamu použite binárne kódy s premenlivou dĺžkou.

Pomohlo veľa online generátor Huffmanove kódy. Len za pár minút sme boli schopní vybrať požadované kódy s premenlivou dĺžkou.

Popis formátu ukladania údajov

Poradie bajtov

Polia väčšie ako jeden bajt sú uložené vo formáte big-endian (sieťové poradie bajtov), ​​to znamená, že 0x1234 sa zapíše ako 0x12, 0x34.

Stránkovanie

Všetka flash pamäť je rozdelená na stránky rovnakej veľkosti.

Predvolená veľkosť stránky je 32 kB, ale nie viac ako 1/4 celkovej veľkosti pamäťového čipu (pre 4 MB čip sa získa 128 strán).

Každá stránka ukladá údaje nezávisle od ostatných (to znamená, že údaje na jednej stránke neodkazujú na údaje na inej stránke).

Všetky stránky sú očíslované v prirodzenom poradí (vo vzostupnom poradí adries), počnúc číslom 0 (nula stránky začína na adrese 0, prvá stránka začína na 32 Kb, druhá strana začína na 64 Kb atď.)

Pamäťový čip sa používa ako cyklická vyrovnávacia pamäť (ring buffer), to znamená, že najskôr sa zapisuje na stránku číslo 0, potom číslo 1, ..., keď vyplníme poslednú stranu, začne sa nový cyklus a nahrávanie pokračuje od strany nula .

Vo vnútri stránky

Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash
Na začiatku stránky je uložená 4-bajtová hlavička stránky, potom kontrolný súčet hlavičky (CRC-32C), potom sú záznamy uložené vo formáte „hlavička, údaje, kontrolný súčet“.

Názov stránky (na obrázku je špinavá zelená) pozostáva z:

  • dvojbajtové pole magického čísla (tiež označenie verzie formátu)
    pre aktuálnu verziu formátu sa počíta ako 0xed00 ⊕ номер страницы;
  • dvojbajtové počítadlo „Verzia stránky“ (číslo cyklu prepisovania pamäte).

Záznamy na stránke sú uložené v komprimovanej forme (používa sa deflačný algoritmus). Všetky záznamy na jednej stránke sú komprimované v jednom vlákne (používa sa bežný slovník) a na každej novej stránke sa kompresia začína odznova. To znamená, že na dekomprimovanie akéhokoľvek záznamu sú potrebné všetky predchádzajúce záznamy z tejto stránky (a iba táto).

Každý záznam bude komprimovaný s príznakom Z_SYNC_FLUSH a na konci komprimovaného toku budú 4 bajty 0x00, 0x00, 0xff, 0xff, pred ktorými môže byť jeden alebo dva ďalšie nulové bajty.
Túto sekvenciu (dlhú 4, 5 alebo 6 bajtov) pri zápise do flash pamäte zahodíme.

Hlavička záznamu má 1, 2 alebo 3 bajty a obsahuje:

  • jeden bit (T) označujúci typ záznamu: 0 - kontext, 1 - log;
  • pole s premenlivou dĺžkou (S) od 1 do 7 bitov, ktoré definuje dĺžku záhlavia a „konca“, ktoré sa musia pridať do záznamu na dekompresiu;
  • dĺžka záznamu (L).

Tabuľka hodnôt S:

S
Dĺžka hlavičky, bajty
Zahodené pri zápise, bajt

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)

Snažil som sa ilustrovať, neviem, ako jasne to dopadlo:
Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash
Žltá tu označuje pole T, biela pole S, zelená L (dĺžka komprimovaných údajov v bajtoch), modrá komprimované údaje, červená posledné bajty komprimovaných údajov, ktoré sa nezapisujú do pamäte flash.

Do jedného bajtu teda môžeme zapisovať hlavičky záznamov najbežnejšej dĺžky (do 63+5 bajtov v komprimovanej forme).

Po každom zázname sa uloží kontrolný súčet CRC-32C, v ktorom sa ako počiatočná hodnota (init) použije prevrátená hodnota predchádzajúceho kontrolného súčtu.

CRC má vlastnosť „trvanie“, funguje nasledujúci vzorec (plus alebo mínus bitová inverzia v procese): Moja implementácia prstencovej vyrovnávacej pamäte v NOR flash.
To znamená, že v skutočnosti vypočítame CRC všetkých predchádzajúcich bajtov hlavičiek a údajov na tejto stránke.

Priamo za kontrolným súčtom je hlavička nasledujúceho záznamu.

Hlavička je navrhnutá tak, že jej prvý bajt je vždy iný ako 0x00 a 0xff (ak namiesto prvého bajtu hlavičky narazíme na 0xff, tak to znamená, že ide o nevyužitú oblasť; 0x00 signalizuje chybu).

Príklady algoritmov

Čítanie z pamäte Flash

Akékoľvek čítanie prichádza s kontrolou kontrolného súčtu.
Ak sa kontrolný súčet nezhoduje, čítanie sa niekoľkokrát zopakuje v nádeji, že sa prečítajú správne údaje.

(to dáva zmysel, Linux neukladá čítania z NOR Flash, testované)

Zápis do flash pamäte

Údaje zaznamenávame.
Poďme si ich prečítať.

Ak sa načítané údaje nezhodujú so zapísanými, oblasť vyplníme nulami a signalizujeme chybu.

Príprava nového mikroobvodu na prevádzku

Pre inicializáciu sa na prvú (alebo skôr nulovú) stranu zapíše hlavička s verziou 1.
Potom sa na túto stránku zapíše počiatočný kontext (obsahuje UUID počítača a predvolené nastavenia).

To je všetko, flash pamäť je pripravená na použitie.

Nabíjanie stroja

Pri načítaní sa prečíta prvých 8 bajtov z každej stránky (hlavička + CRC), ignorujú sa stránky s neznámym magickým číslom alebo nesprávnym CRC.
Zo „správnych“ strán sa vyberú strany s maximálnou verziou a z nich sa vyberie strana s najvyšším číslom.
Prečíta sa prvý záznam, skontroluje sa správnosť CRC a prítomnosť príznaku „kontext“. Ak je všetko v poriadku, táto stránka sa považuje za aktuálnu. Ak nie, vrátime sa späť na predchádzajúcu, kým nenájdeme „živú“ stránku.
a na nájdenej stránke čítame všetky záznamy, tie, ktoré používame s príznakom „kontext“.
Uložte slovník zlib (bude potrebný na pridanie na túto stránku).

To je všetko, sťahovanie je dokončené, kontext je obnovený, môžete pracovať.

Pridanie položky denníka

Záznam skomprimujeme pomocou správneho slovníka so špecifikáciou Z_SYNC_FLUSH. Uvidíme, či sa skomprimovaný záznam zmestí na aktuálnu stránku.
Ak sa nezmestí (alebo sa na stránke vyskytli chyby CRC), začnite novú stránku (pozri nižšie).
Zapisujeme záznam a CRC. Ak sa vyskytne chyba, začnite novú stránku.

Nová stránka

Vyberieme voľnú stránku s minimálnym počtom (za voľnú stránku považujeme stránku s nesprávnym kontrolným súčtom v hlavičke alebo s verziou menšou ako aktuálna). Ak takéto stránky neexistujú, vyberte stránku s minimálnym počtom z tých, ktoré majú verziu rovnajúcu sa aktuálnej.
Vymazanú stránku vymažeme. Obsah skontrolujeme pomocou 0xff. Ak niečo nie je v poriadku, zoberte ďalšiu bezplatnú stránku atď.
Na vymazanú stránku napíšeme hlavičku, prvý záznam je aktuálny stav kontextu, ďalší je nezapísaný záznam denníka (ak existuje).

Použiteľnosť formátu

Podľa mňa sa ukázal ako dobrý formát na ukladanie akýchkoľvek viac či menej komprimovateľných informačných tokov (obyčajný text, JSON, MessagePack, CBOR, prípadne protobuf) v NOR Flash.

Formát je samozrejme „šitý na mieru“ pre SLC NOR Flash.

Nemal by sa používať s médiami s vysokým BER, ako sú NAND alebo MLC NOR (je takáto pamäť vôbec dostupná na predaj? Videl som ju spomenutú iba v prácach o opravných kódoch).

Okrem toho by sa nemal používať so zariadeniami, ktoré majú vlastný FTL: USB flash, SD, MicroSD atď (pre takúto pamäť som vytvoril formát s veľkosťou strany 512 bajtov, podpisom na začiatku každej strany a jedinečnými číslami záznamov - niekedy bolo možné obnoviť všetky dáta z „chybového“ flash disku jednoduchým sekvenčným čítaním).

V závislosti od úloh je možné formát použiť bez zmien na flash diskoch od 128Kbit (16Kb) do 1Gbit (128MB). Ak chcete, môžete ho použiť na väčšie čipy, ale pravdepodobne budete musieť upraviť veľkosť stránky (Tu však už vyvstáva otázka ekonomickej uskutočniteľnosti; cena za veľkoobjemový NOR Flash nie je povzbudivá).

Ak niekoho formát zaujme a chce ho použiť v otvorenom projekte, napíšte, skúsim nájsť čas, vyleštím kód a uverejním ho na github.

Záver

Ako vidíte, formát sa nakoniec ukázal ako jednoduchý a dokonca nudné.

Je ťažké vyjadriť vývoj môjho pohľadu v článku, ale verte mi: pôvodne som chcel vytvoriť niečo sofistikované, nezničiteľné, schopné prežiť aj jadrový výbuch v tesnej blízkosti. Rozum však (dúfam) predsa len zvíťazil a postupne sa priority posunuli smerom k jednoduchosti a kompaktnosti.

Je možné, že som sa mýlil? Áno samozrejme. Môže sa napríklad ukázať, že sme kúpili dávku nekvalitných mikroobvodov. Alebo z nejakého iného dôvodu zariadenie nespĺňa očakávania spoľahlivosti.

Mám na to plán? Myslím, že po prečítaní článku nepochybujete, že existuje plán. A dokonca ani sám.

Trochu vážnejšie je, že formát bol vyvinutý ako pracovná možnosť aj ako „skúšobný balón“.

V súčasnosti všetko na stole funguje dobre, doslova druhý deň bude riešenie nasadené (približne) na stovkách zariadení sa pozrime, čo sa stane v „bojovej“ prevádzke (našťastie, dúfam, že vám formát umožňuje spoľahlivo odhaliť zlyhania; takže môžete zbierať úplné štatistiky). O niekoľko mesiacov bude možné vyvodiť závery (a ak budeš mať smolu, tak aj skôr).

Ak sa na základe výsledkov používania objavia vážne problémy a budú potrebné zlepšenia, určite o tom napíšem.

Literatúra

Nechcel som robiť dlhý únavný zoznam použitých diel, veď každý má Google.

Tu som sa rozhodol zanechať zoznam zistení, ktoré sa mi zdali obzvlášť zaujímavé, no postupne migrovali priamo do textu článku a na zozname zostala jedna položka:

  1. Užitočnosť infgen od autora zlib. Dokáže prehľadne zobraziť obsah archívov deflate/zlib/gzip. Ak sa musíte popasovať s vnútornou štruktúrou formátu deflate (alebo gzip), vrelo odporúčam.

Zdroj: hab.com

Pridať komentár