Minu NOR flash'i ringbuferi rakendus

Eelalugu

Meil on oma arendatud automaadid. Sees on Raspberry Pi ja mĂ”ningane lisaseade eraldi plaadil. Ühendatud on mĂŒntide vastuvĂ”tja, pangakaarditud, pangaterminal jne. KĂ”ike juhib omakĂ€eliselt kirjutatud programm. KĂ”ik tööajalugu salvestatakse USB-mĂ€lupulgale (MicroSD), mis edastatakse seejĂ€rel interneti kaudu (kasutades USB-modem) serverisse, kus see salvestatakse andmebaasi. MĂŒĂŒgiteave laaditakse 1C-sse, samuti on olemas lihtne veebiliides jĂ€lgimiseks jne.

Seega on ajakiri hĂ€davajalik – arvestuseks (seal on tulu, mĂŒĂŒgid jne), jĂ€lgimiseks (kĂ”ikvĂ”imalikud tĂ”rked ja muud ootamatud olukorrad); see on, vĂ”ib öelda, kogu teave, mis meil selle automaadi kohta on.

Probleem

MĂ€lupulgad on nĂ€idanud end vĂ€ga ebastabiilsete seadmetena. Need riknevad ĂŒllataval mÀÀral. See pĂ”hjustab nii automaatide seiskumisi kui ka (kui mingil pĂ”hjusel ajakirja ei suudetud veebis edastada) andmete kaotust.

See on juba mitte esimene kogemus mĂ€lupulkade kasutamisel; eelmisel projektis oli ĂŒle saja seadme, kus ajalugu salvestati USB-mĂ€lupulkadel. Seal olid samuti usaldusprobleemid ja mĂ”nikord tuli kuus vĂ€lja vahetada kĂŒmneid. Proovisime erinevaid mĂ€lupulkasid, sealhulgas brĂ€ndipulki SLC mĂ€luga; jah, mĂ”ned mudelid on usaldusvÀÀrsemad kui teised, kuid mĂ€lupulkade vahetamine ei lahendanud probleemi kardinaalselt.

TÀhelepanu! Pikem artikkel! Kui teid ei huvita 'miks', vaid ainult 'kuidas', vÔite kohe minna lÔppu artiklist.

Lahendus

Esimene mÔte: loobuda MicroSD-st, paigaldada nÀiteks SSD ja bootida sellest. Teoorias on see vÔimalik, vÔib-olla, kuid suhteliselt kallis ja mitte eriti usaldusvÀÀrne (lisandub USB-SATA adapter; ka odavate SSD-de rike on statistiliselt murettekitav).

USB HDD ei tundu samuti eriti atraktiivne lahendus.

SeetĂ”ttu jĂ”udsime sellise lahenduseni: jĂ€tta bootimine MicroSD-st, kuid kasutada neid kirjutamisest koosnevas reĆŸiimis ja salvestada tööajalugu (ja muu konkreetse seadme jaoks unikaalne teave — seerianumber, sensorite kalibreerimine jne) kuskile mujale.

Read-only fail-safe teema Raspberry Pi jaoks on juba pĂ”hjalikult uuritud, ma ei hakka selles artiklis rakenduse ĂŒksikasjadele peatuma. (kuid kui huvi on — ehk kirjutan sellel teemal mini-artikli).. Üks asi, mida sooviksin mĂ€rkida: nii isikliku kogemuse kui ka nende tagasiside pĂ”hjal, kes on juba rakendanud, on usaldusvÀÀrsuse kasu olemas. Jah, tĂ€ielikult rikkeid vĂ€ltida ei saa, kuid nende tekkimise sagedust on tĂ€iesti vĂ”imalik oluliselt vĂ€hendada. Ja ka kaardid muutuvad ĂŒhtseks, mis lihtsustab hooldustöötajate vahetust oluliselt.

Riistvara osa

MĂ€lu tĂŒĂŒbi valikuga ei olnud erilisi kahtlusi — NOR Flash.
Argumentide loetelu:

  • Lihtne ĂŒhendamine (tavaliselt SPI buss, mille kasutamise kogemus on juba olemas, seega „riistvara“ probleeme ei ole oodata);
  • naljakas hind;
  • standardne tööprotokoll (rakendus on juba Linuxi tuumas olemas, soovi korral saab kasutada ka kolmanda osapoole lahendusi, mida samuti leidub, vĂ”i isegi kirjutada enda, sest kĂ”ik on lihtne);
  • usaldusvÀÀrsus ja ressursid:
    tĂŒĂŒpilisest andmelehtest: andmed sĂ€ilivad 20 aastat, 100000 kustutust iga ploki jaoks;
    kolmandatest allikatest: ÀÀrmiselt madal BER, vĂ€idetakse, et vigade paranduskoodide vajadust ei ole. (mĂ”nedes töödes kĂ€sitletakse ECC-d NOR-i jaoks, kuid enamasti mĂ”eldakse siiski MLC NOR-i, kĂŒll aga on see vĂ”imalik).

Vaatame nÔudeid mahu ja ressursi osas.

Tahame, et andmed sĂ€iliksid soovitud vĂ€hemalt paariks pĂ€evaks. See on vajalik selleks, et ajaloolised mĂŒĂŒgid ei kaoks, kui tekib mingit tĂŒĂŒpi suhtlusprobleeme. Suundume viiele pĂ€evale, selle ajaga (isegi puhkepĂ€evade ja pĂŒhade arvestamisel) on probleem lahendatav.

Meil koguneb praegu pĂ€evas umbes 100 kb logi (3-4 tuhat kirjet), kuid see number kasvab jĂ€rk-jĂ€rgult – detailide suurenemise ja uute sĂŒndmuste lisandumise tĂ”ttu. Lisaks on aeg-ajalt ka tĂ”ususid (nĂ€iteks mĂ”ni andur hakkab valehĂ€ireid genereerima). Arvutame 10 tuhande kirje pealt, 100 baidi kohta - 1 megabait pĂ€evas.

Kokku tuleb 5 MB puhtaid (hÀsti tihendatavaid) andmeid. Nendele lisandub veel (karm hinnang) 1 MB administraatiivandmeid.

See tĂ€hendab, et vajame 8 MB kiipi, kui tihendamist ei kasutata, vĂ”i 4 MB, kui kasutatakse. Need numbrid on tĂ€iesti realistlikud selle tĂŒĂŒpi mĂ€lu jaoks.

Ressursi osas: kui me eeldame, et mĂ€lu kirjutatakse tĂ€ielikult ĂŒle mitte sagedamini kui iga 5 pĂ€eva tagant, siis 10-aastase kasutuse jooksul saame vĂ€hem kui tuhat ĂŒle kirjutamise tsĂŒklit.
Tuletan meelde, et tootja lubab sada tuhat.

Veidi NOR vs NAND kohta

Praegu on kindlasti palju populaarsem NAND-mÀlu, kuid selle projekti puhul ei soovita ma seda kasutada: NAND, erinevalt NOR-ist, nÔuab tingimata vigade paranduse koode, vigaste plokkide tabelit jne, ja NAND-kiipide jalgade arv on tavaliselt palju suurem.

NOR-i puudustena vÔib vÀlja tuua:

  • vĂ€ike maht (ja seega kĂ”rge hind megabaidi kohta);
  • keskmine edastuskiirus (suures osas tĂ€nu sellele, et kasutatakse jĂ€rjestikulist liidest, tavaliselt SPI vĂ”i I2C);
  • aeglane kustutamine (sĂ”ltuvalt ploki suurusest vĂ”ib see vĂ”tta osahÀÀlte sekunditest kuni mitme sekundini).

Dramatiseerimata, jÀtkame.

Kui ĂŒksikasjad huvitavad, on valitud kiip at25df321a (kuid siiski on see ebaoluline, turul on hulk analooge, mis on ĂŒhenduskaabli ja kĂ€susĂŒsteemiga ĂŒhilduvad; isegi kui soovime paigaldada kiibi teiselt tootjalt ja/vĂ”i teises mahus, töötab kĂ”ik ilma koodi muutmiseta).

Kasutame Linuxi tuuma sisse ehitatud draiverit, Raspberryl on seadme puu katte toe tĂ”ttu kĂ”ik vĂ€ga lihtne — piisab, kui panna /boot/overlaysse kompileeritud overlay ja veidi muuta /boot/config.txt faili.

DTS faili nÀidis

Olles aus, pole ma kindel, et see on kirjutatud ilma vigadeta, aga 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 failis

dtoverlay=at25:spimaxfrequency=50000000

Kiibi ĂŒhendamise kirjeldust Raspberry Pi-le jĂ€tan vahele. Ühest kĂŒljest pole ma elektroonika spetsialist, teisest kĂŒljest on see isegi minu jaoks elementaarne: kiibil on ainult 8 jalga, millest me vajame vaid maapinda, toidet, SPI (CS, SI, SO, SCK); tasemed sobivad Raspberry Pi omadega, tĂ€iendavat sidumist ei ole vaja — lihtsalt ĂŒhendage kuus kontaktpunkti.

Ülesande seadmine

Nagu tavaliselt, lĂ€bib ĂŒlesande seadmine mitmeid iteratsioone, mulle tundub, et on aeg jĂ€rgmise jaoks. Nii et lĂ”petame, koondame selle, mis on juba kirjutatud, ja selgitame vĂ€lja varjatud ĂŒksikasjad.

Nii et, oleme otsustanud, et ajakiri salvestatakse SPI NOR Flash'i.

Mis on NOR Flash, kui keegi ei tea

See on energiat mittevajav mÀlu, millega saab teha kolme toimingut:

  1. Lugemine:
    Tavaline lugemine: edastame aadressi ja loeme nii palju baitide, kui vajame;
  2. Salvestamine:
    Kirjutamine NOR flash'i nĂ€eb vĂ€lja nagu tavaline, kuid sellel on ĂŒks omadus: saab muuta ainult 1-ks 0, mitte vastupidi. NĂ€iteks, kui meil on mĂ€lu lahtris 0x55, siis pĂ€rast 0x0f kirjutamist sinna, jÀÀb seal hoitavaks 0x05 (vt tabelit allpool);
  3. Kustutamine:
    Muidugi, peame suutma teha ka vastupidist operatsiooni — muuta 0-ks 1, selle jaoks ongi olemas kustutamise toiming. Erinevalt esimestest kahest toimingust, pĂ”hineb see blokki, mitte baitidel (valitud mikroskeemi minimaalne kustutus blokk on 4kb). Kustutamine hĂ€vitab kogu blokki tervikuna ja see on ainus viis, kuidas muuta 0-ks 1. SeetĂ”ttu tuleb flash-mĂ€luga töötades sageli andmestruktuure joondada kustutusplokkide piirile.
    Kirjutamine NOR Flash'i:

Binaarsed andmed

Oli
01010101

Oleme kirjutanud
00001111

Kohandatud
00000101

Ajakiri ise on muutuva pikkusega kirjeid jĂ€rjestus. TĂŒĂŒpiline kirje pikkus on umbes 30 baiti (kuigi mĂ”nikord juhtub, et kirjed on mitu kilobaiti pikad). Antud juhul töötame nendega lihtsalt kui baitide kogumiga, kuid kui huvitab, siis salvestustes kasutatakse CBOR-i.

Lisaks logile peame sĂ€ilitama teatud „konfigureerimis” teavet, nii uuendatavat kui ka mitte: mingi seadme ID, andurite kalibreerimine, lipp „seade on ajutiselt vĂ€lja lĂŒlitatud”, jne.
See teave koosneb key-value запОсidest, mis on samuti salvestatud CBOR-i. Meil ei ole seda teavet vÀga palju (maksimaalselt paar kilobaiti), see uuendatakse harva.
Hiljem nimetame seda kontekstiks.

Kui meenutada, kust see artikkel algas, on vÀga oluline tagada andmete sÀilitamise usaldusvÀÀrsus ja, kui vÔimalik, katkematu töö isegi riistvaraharvikute/debiitide korral.

Milliseid probleemiallikasid saaks kaaluda?

  • Toitekatkestus kirjutamise-/kustutamisoperatsioonide ajal. See kuulub sellesse kategooriasse, et "lĂŒli ei saa vastu vĂ”tta trikki".
    Teave pĂ€rit esitati stackexchange'is: toitekatkestuse ajal flash-mĂ€luga töötamisel, kui toide katkeb, viib erase (1-seadistus) ja write (0-seadistus) mÀÀramatute kĂ€itumisteni: andmed vĂ”ivad kirjutada, kirjutada osaliselt (ĂŒtleme, et edastasime 10 baiti/80 bitti, kuid suudame kirjutada ainult 45 bitti), ei ole vĂ€listatud, et osa bitte on "vaheolekus" (lugemine vĂ”ib anda nii 0 kui ka 1);
  • Flash-mĂ€lu enda vead.
    BER ehk vÀga madal, aga ei saa olla null;
  • Vead bussil
    Andmed, mis edastatakse SPI kaudu, ei ole kuidagi kaitstud, vĂ”ivad esineda nii ĂŒksikud bitti vead kui ka sĂŒnkroonimisvead — bitide kadumine vĂ”i lisamine (mis viib massiliste andmete moonutusteni);
  • Muud vead/tĂ”rked
    Koodivead, Raspberry 'glitchid', tulnukate sekkumine...

Olen sÔnastanud nÔudmised, mille tÀitmine on minu arvates vajalik usaldusvÀÀrsuse tagamiseks:

  • kirjutised peavad jĂ”udma flash-mĂ€llu kohe, viivitusega kirjutamist ei arvestata; - kui viga tekib, peab see tuvastama ja töötlema vĂ”imalikult vara; - sĂŒsteem peab vĂ”imaluse korral taastama töö pĂ€rast vigade ilmnemist.
    (nĂ€ide elust «kuidas mitte teha», millega arvan, et kĂ”ik on kokku puutunud: pĂ€rast hĂ€daolukorra taaskĂ€ivitamist «hĂ€vis» failisĂŒsteem ja operatsioonisĂŒsteem ei kĂ€ivitu)

Ideed, lÀhenemised, mÔtisklused

Kui hakkasin sellele probleemile mÔtlema, jooksid peast lÀbi hulgaliselt ideid, nÀiteks:

  • kasutada andmete tihendamist;
  • kasutada nutikaid andmestruktuure, nĂ€iteks hoida kirjepealkirjad eraldi kirjetest, et vigastuse korral mĂ”nes kirjes saaks ĂŒlejÀÀnud probleemideta lugeda;
  • kasutada bitivĂ€lju kirje lĂ”petatuse kontrollimiseks toite katkestamise ajal;
  • hoida kontrollsummasid kĂ”ikjal;
  • kasutada mĂ”nda sorti vigade parandamise kodeerimist.

Osa neist ideedest rakendati, osa otsustati kÔrvale jÀtta. Alustame jÀrjekorras.

Andmete tihendamine

KĂŒlastatud sĂŒndmused, mida me logime, on piisavalt ĂŒhtekuuluvad ja korduvad («viskasime mĂŒndi 5 rubla», «vajutasime tagastusnupu», 
). SeetĂ”ttu peaks tihendamine olema piisavalt efektiivne.

Surve kulud kokkusurumise puhul on ebaolulised (meil on piisavalt vĂ”imas protsessor, isegi esimesel Pi-l oli ĂŒks 700 MHz tuum, tĂ€napĂ€evastes mudelites on mitu tuuma ĂŒle ĂŒhe gigaherti), andmete vahetamise kiirus salvestusega ei ole kĂ”rge (mĂ”ned megabaitides sekundis), kirjete suurus on vĂ€ike. KokkuvĂ”ttes, kui kokkusurumine mĂ”jutab jĂ”udlust, siis ainult positiivselt. (absoluutselt mitte kriitiline, lihtsalt konstateerin). Pluss, meil ei ole ju tegelikult embedded, vaid tavaline Linux — seega ei tohiks rakendamine palju vaeva nĂ”uda (piisab lihtsalt raamatukogu liitmisest ja mĂ”ne funktsiooni kasutamisest sealt).

VÔeti proov logifailist töötavast seadmest (1,7 MB, 70 tuhat kirjet) ja alguses kontrolliti seda kokkusurumise osas olemasolevate gzipi, lz4, lzop, bzip2, xz ja zstd abil.

  • gzip, xz, zstd nĂ€itasid sarnaseid tulemusi (40 KB).
    Üllatusena nĂ€itas moes xz end siin gzipi vĂ”i zstd tasemel;
  • lzip vaikeseadetega andis veidi halvemad tulemused;
  • lz4 ja lzop nĂ€itasid mitte eriti hĂ€id tulemusi (150 KB);
  • bzip2 nĂ€itas ĂŒllatavalt hĂ€id tulemusi (18 KB).

Seega andmed kokku surutakse vÀga hÀsti.
Seega (kui me ei leia tÔsiseid vigu) on tihendamine vÀltimatu! Lihtsalt sellepÀrast, et sama mÀlupulgale mahub rohkem andmeid.

MÔtleme puudustele.

Esimene probleem: me oleme juba kokku leppinud, et iga kirje peab kohe mÀlupulgale minema. Tavaline arhiveerija kogub andmeid sisendvoolust, kuni otsustab, et on aeg kirjutada vÀljundisse. Meie peame kohe saama tihendatud andmeploki ja salvestama selle mittevolatile mÀllu.

NĂ€en kolme teed:

  1. Tihendada iga kirje sÔnastiku tihendamise abil, selle asemel et kasutada eelnevalt arutatud algoritme.
    Töötav variant, kuid mulle see ei meeldi. Et tagada enam-vĂ€hem korralik tihendustase, peab sĂ”nastik olema konkreetsete andmete jaoks "hÀÀlestatud"; iga muutus pĂ”hjustab, et tihendustase langeb katastroofiliselt. Jah, probleemi saab lahendada uue sĂ”nastiku versiooni loomisega, kuid see on peavalu — meil tuleb hoida kĂ”iki sĂ”nastiku versioone; igas kirjes tuleb mĂ€rkida, millise versiooniga sĂ”nastik see oli tihendatud

  2. Tihendada iga kirje "klassikaliste" algoritmide abil, kuid sÔltumatult teistest.
    Kasutatavad kokkusurumise algoritmid ei ole ette nĂ€htud sellise suuruse (kĂŒmnete baitide) salvestuste töötlemiseks, kokkusurumise koefitsiendiks jÀÀb selgelt alla 1 (st andmete maht suureneb hoopis, mitte ei vĂ€hene);
  3. Teha FLUSH pÀrast iga salvestust.
    Paljudes kokkusurumisbiblioteekides on FLUSH-i tugi. See on kĂ€sk (vĂ”i parameeter kokkusurumisprotseduurile), mille saanud, arhiivija loob kokkusurutud voogu nii, et selle alusel on vĂ”imalik taastada kĂ”ik dekompressitud andmed, mis on juba saadud. Selline analoog sync failisĂŒsteemides vĂ”i commit SQL-is.
    Oluline on, et hilisemad kokkusurumised saaksid kasutada akumuleeritud sÔnastikku ja kokkusurumise aste ei kannataks nii palju kui eelmises variandis.

MÔtlen, et on ilmne, et valisin kolmanda variandi, peatume sellel lÀhemalt.

Leiti suurepÀrane artikkel FLUSH-ist zlib-is.

Tehtud inspiratsioonina artiklist katsetest, vÔtsin 70 tuhat logisaldot reaalsetelt seadmetelt, lehe suuruseks 60Kb (lehe suuruse juurde me veel tagasi tuleme) sain:

Algandmed
gzip kokkusurumine -9 (ilma FLUSH-ita)
zlib Z_PARTIAL_FLUSH-iga
zlib Z_SYNC_FLUSH-iga

Maht, Kb
1692
40
352
604

Esmapildist nĂ€ib FLUSH hind ĂŒlemÀÀra kĂ”rge, kuid tegelikult on meie valik tagasihoidlik – kas mitte ĂŒldse kokku suruda vĂ”i suruda (ja vĂ€ga tĂ”husalt) FLUSH-iga. Ei tohi unustada, et meil on 70 000 kirjet, Z_PARTIAL_FLUSH toob kaasa vaid 4-5 baiti ĂŒleliigsust iga kirje kohta. Kompressioonikordaja osutus peaaegu 5:1, mis on suurepĂ€rane tulemus.

See vĂ”ib tunduda ĂŒllatav, kuid tegelikult on Z_SYNC_FLUSH tĂ”husam viis FLUSH-i tegemiseks.

Kui kasutada Z_SYNC_FLUSH-i, siis iga kirje 4 viimast baiti on alati 0x00, 0x00, 0xff, 0xff. Ja kui need meile teada on – siis me ei pea neid sĂ€ilitama, mis tĂ€hendab, et lĂ”plik suurus on vaid 324Kb.

Viidatud artiklis on seletus:

Lisatakse tĂŒĂŒp 0 plokk, mille sisu on tĂŒhi.

TĂŒĂŒp 0 plokk tĂŒhi sisu koosneb:

  • kolmest bitist koosnev plokipealkiri;
  • 0 kuni 7 bitti, mis on vĂ”rdsed nulliga, et saavutada baitide joondamine;
  • neli baiti jĂ€rjestus 00 00 FF FF.

Nagu ei ole raske mÀrgata, eelviimasel plokil 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 (kas alati?) ploki tĂŒĂŒbiga 1 (fiksplokk), mis peab lĂ”ppema 7 nullbitiga, seega saame kokku 10-17 garanteeritud nullbitti (ja ĂŒlejÀÀnud on tĂ”enĂ€oliselt nullid umbes 50% tĂ”enĂ€osusega).

Seega, testandmete puhul esinevad 100% juhtudest enne 0x00, 0x00, 0xff, 0xff ĂŒks nullbait, ja enam kui kolmandal juhul — kaks nullbait. (vĂ”ib-olla on asi selles, et kasutan binaarset CBOR-i, ja teksti JSON-i kasutamisel esineks sagedamini 2. tĂŒĂŒpi blokke — dĂŒnaamiline plokk, seega esineks blokke ilma lisanduvate nullbaitideta enne 0x00, 0x00, 0xff, 0xff)..

KokkuvÔttes saab olemasolevate testandmete pÔhjal mahtuda alla 250 KB komprimeeritud andmeid.

Saame veel veidi kokku hoida, tegeledes bittide jongleerimisega: praegu ignoreerime bloki lÔpus mitme nullbiti olemasolu, samuti ei muutu mÔned bitid bloki alguses

Aga siin tegin ma tahteotsuse lÔpetada, muidu vÔiksin sellise tempoga enda arhivaatori arendamiseni jÔuda.

KokkuvĂ”ttes sain oma testandmetest 3–4 baiti salvestuse kohta, tihendamise suhe oli ĂŒle 6:1. Pean ausalt tunnistama, et ei oodanud sellist tulemust; minu arvates on kĂ”ik, mis on parem kui 2:1, juba tulemuseks, mis Ă”igustab tihendamise kasutamist.

KĂ”ik on suurepĂ€rane, kuid zlib (deflate) on siiski aegunud, vÀÀrikas ja veidi vanamoodne tihendamisalgoritm. Üksnes see, et sĂ”nastikuks kasutatakse viimaseid 32 Kb tihendamata andmete voost, tundub tĂ€napĂ€eval kummaline (st kui mĂ”ni andmeplokk on vĂ€ga sarnane sellele, mis oli sisendis 40 Kb tagasi, siis hakkab see uuesti arhiveerima, mitte ei viita eelmisele esinemisele). Moodsa arhiveerimise tarkvara puhul mÔÔdetakse sĂ”nastiku suurust tihti megabaitides, mitte kilobaitides.

Nii et jÀtkame meie mini-uuringut arhiveerimiste kohta.

JÀrgmine proovitud oli bzip2 (meenutan, et ilma FLUSHita nÀitas see fantastilist tihendamise taset, peaaegu 100:1). Kahjuks FLUSHiga nÀitas see end vÀga halvasti, tihendatud andmete suurus osutus suuremaks kui tihendamata andmete suurus.

Minu oletused ebaedukuse pÔhjustest

Libbz2 pakub ainult ĂŒhte flush varianti, mis nĂ€ib kustutavat sĂ”nastiku (sarnane Z_FULL_FLUSH-ile zlib-s), peale selle on raske rÀÀkida mingist tĂ”husast tihendamisest.

Viimaseks prooviti zstd. SÔltuvalt seadistustest suudab see tihendada kas gzip tasemel, kuid palju kiiremini, vÔi isegi paremini kui gzip.

Kahjuks ei nÀidanud see koos FLUSH-iga end kuigi hÀsti: tihendatud andmete suuruseks tuli umbes 700 Kb.

Mina esitasin kĂŒsimuse projekti lehelt githubis ja sain vastuseks, et vĂ”iks arvestada umbes 10 baiti haldusteavet iga tihendatud andmeploki kohta, mis on lĂ€hedane saadud tulemusele, deflate’i ei suuda kuidagi jĂ”uda.

Sellega otsustasin arhiivimise katsetega lÔpetada (meeldetuletuseks, xz, lzip, lzo, lz4 ei nÀidanud end veel FLUSH-ita testimise etapis hÀsti, ja ma ei hakanud arvestama eksootilisemaid tihendusalgoritme).

Naaseme arhiivimise probleemide juurde.

Teine probleem (nagu öeldakse, jĂ€rjekorra mĂ”ttes, mitte tĂ€henduses) seisneb selles, et tihendatud andmed kujutavad endast ĂŒhtset voogu, kus pidevalt viidatakse varasematele osadele. Seega, kui mingi osa tihendatud andmetest saab kahjustada, kaotame mitte ainult sellega seotud mittetihendatud andmepaki, vaid ka kĂ”ik jĂ€rgnevad.

Selle probleemi lahendamiseks on mitmeid lÀhenemisviise:

  1. Probleemi ilmnemise ennetamine — lisada tihendatud andmetesse ĂŒleliigset teavet, mis vĂ”imaldab vigu tuvastada ja parandada; sellest rÀÀgime hiljem;
  2. Minimeerida tagajÀrgi, kui probleem siiski tekib.
    Oleme varem rÀÀkinud, et iga andmepaki saab tihendada iseseisvalt, sel juhul probleem ise kaob (ĂŒhe paketi andmete kahjustamine toob kaasa andmete kaotuse ainult selle paketi puhul). Kuid see on ÀÀrmuslik juhtum, kus andmete tihendamine on ebaefektiivne. Teine ÀÀrmus on kasutada kĂ”iki 4 MB meie mikroskeemi ĂŒheks arhiiviks, mis annab meile suurepĂ€rase tihenduse, kuid katastroofilised tagajĂ€rjed andmete kahjustumise korral.
    Jah, usaldusvÀÀrsuse osas on vajalik kompromiss. Kuid tuleb meeles pidada, et arendame andmete salvestamise formaati energiaefektiivsel mÀlul, millel on ÀÀrmiselt madal BER ja deklareeritud andmete sÀilitamise aeg 20 aastat.

Katsete kÀigus avastasin, et mÀrkimisvÀÀrsemad tihendustaseme kadumised algavad tihendatud andmeplokkide puhul, mille suurus on alla 10 Kb.
Varem mainiti, et kasutatav mĂ€lu on lehekĂŒlje struktuuriga, ma ei nĂ€e pĂ”hjust, miks ei peaks kasutama vastavust "ĂŒks leht - ĂŒks tihendatud andmeplokk".

See tÀhendab, et minimaalne mÔistlik lehe suurus on 16 Kb (koos varuga teenindusteabe jaoks). Kuid nii vÀike lehe suurus seab olulisi piiranguid maksimaalse kirje suurusele.

Kuigi mul ei ole praegu ette nĂ€htud kirjeid, mis ĂŒletaksid ĂŒhte kilobaiti tihendatud kujul, otsustasin kasutada lehti, mille suurus on 32 Kb (kokku saab 128 lehte kiibi kohta).

KokkuvÔte:

  • Andmeid salvestame zlib (deflate) abil tihendatuna;
  • Iga kirje puhul seadistame Z_SYNC_FLUSH;
  • Iga tihendatud kirje lĂ”pus kĂ€rpime viimased baitid (nĂ€iteks 0x00, 0x00, 0xff, 0xff); pealkirjas nĂ€itame, kui palju baite me eemaldame;
  • Andmeid salvestatakse lehtedena, igaĂŒhes 32 Kb; lehe sees on pidev surve all olevate andmete voog; iga lehe surumist alustame uuesti.

Ja enne, kui lĂ”petame survet, soovin juhtida tĂ€helepanu sellele, et meil on surutud andmeid vaid mĂ”ned bait kirje kohta, seega on ÀÀrmiselt oluline mitte paisutada teenindusteavet – iga bait on arvel.

Andmepealkirjade hoidmine

Kuna meil on muutliku pikkusega kirjed, peame kuidagi mÀÀrama asukoha/piirid.

Ma tean kolme lÀhenemist:

  1. KÔik kirjed hoitakse pidevas voos, alguses on kirje pealkiri, mis sisaldab pikkust, ja seejÀrel tuleb ise kirje.
    Selles variandis vÔivad nii pealkirjad kui andmed olla muutliku pikkusega.
    Sisuliselt on meil ĂŒhekordne loend, mida kasutatakse laialdaselt;
  2. Pealkirjad ja ise kirjed hoitakse eraldi voogudes.
    Kasutades pĂŒsiva pikkusega pealkirju, saavutame selle, et ĂŒhe pealkirja rike ei mĂ”juta teisi.
    Sarnast lĂ€henemist kasutatakse nĂ€iteks paljudes failisĂŒsteemides;
  3. Kirjed sĂ€ilitatakse pidevas voos, kirje piirid mÀÀratakse teatud mĂ€rgise (sĂŒmboli/jĂ€rjestuse, mis on andmeplokkides keelatud) jĂ€rgi. Kui kirje sees on mĂ€rk, asendame selle mingi jĂ€rjestusega (pĂ”genev).
    Sarnast lÀhenemist kasutatakse nÀiteks PPP protokollis.

Illustreerime.

Variant 1:
Minu NOR flash'i ringbuferi rakendus
Siin on kÔik vÀga lihtne: teades kirje pikkust, saame arvutada jÀrgmise pÀise aadressi. Nii liigume pÀiste kaudu, kuni leiame ala, mis on tÀis 0xff (vabat ruumi) vÔi lehe lÔpu.

Variant 2:
Minu NOR flash'i ringbuferi rakendus
Muutuva kirje pikkuse tĂ”ttu ei saa me ette öelda, kui palju kirjeid (ja seega ka pĂ€iseid) me lehele vajame. PĂ€iseid ja andmeid saab jaotada erinevatele lehtedele, kuid mulle meeldib teine lĂ€henemine: nii pĂ€ised (pĂŒsiva suurusega) saavad olla lehe alguses kui ka andmed (muutva pikkusega) lehe lĂ”pust. Niikaua, kuni nad "kohtuvad" (vaba ruumi ei jĂ€tku uue kirje jaoks), peame seda lehte tĂ€idetuks.

Variant 3:
Minu NOR flash'i ringbuferi rakendus
Siin pole vaja pealkirjas salvestada andmete pikkust vÔi muud asukoha teavet, piisab mÀrkidest, mis tÀhistavad kirje piire. Kuid andmeid tuleb töötleda kirjutamisel / lugemisel.
MÀrgina vÔiksin kasutada 0xff (millega leht pÀrast kustutamist tÀidetakse), nii et vaba ala ei tÔlgendataks andmetena.

VÔrdlustabel:

Valik 1
Variant 2
Variant 3

Vigadele vastupidavus
—
+
+

Kompaktsus
+
—
+

Teostamise keerukus
*
**
**

Variant 1l on kriitiline puudus: kui mĂ”ni pealkiri on kahjustatud, hĂ€vitatakse kogu jĂ€rgneva ahela. ÜlejÀÀnud variandid vĂ”imaldavad taastada osa andmeid isegi massiliste kahjustuste korral.
Siinkohal tasub aga meeles pidada, et otsustasime andmeid sÀilitada kompaktses vormis, seega kaotame me kÔik andmed lehe peal pÀrast "katkist" salvestamist, nii et kuigi tabelis on miinus, ei arvestata seda.

Kompaktsus:

  • esimeses variantis peame pealkirjas salvestama ainult pikkuse, kui kasutada muutuva pikkusega tĂ€isarve, siis enamikul juhtudel saame hakkama ĂŒhe baitiga.
  • teises versioonis peame salvestama algse aadressi ja pikkuse; salvestus peab olema pĂŒsiva suurusega, hindan seda 4 baitiks salvestuse kohta (kaks baiti nihke jaoks ja kaks baiti pikkuse jaoks);
  • kolmandale versioonile piisab vaid ĂŒhest tĂ€hest salvestuse alguse tĂ€histamiseks, pluss ise salvestus kasvab tĂ€nu eskaleerimisele 1-2%. Üldiselt on see esimesega enam-vĂ€hem vĂ”rdsed.

Alguses pidasin teist versiooni pÔhiversiooniks (ja isegi kirjutasin rakenduse). LÔpetasin selle kasutamise vaid siis, kui otsustasin lÔplikult kasutada kompressiooni.

VĂ”ib-olla kunagi kasutan sellist varianti. NĂ€iteks, kui pean tegelema andmete salvestamisega laeva jaoks, mis kĂ€ib Maa ja Marsi vahel — seal on nĂ”uded usaldusvÀÀrsusele tĂ€iesti teisest tasemest, kosmiline kiirgus, 


Mis puutub kolmandasse versiooni: andsin sellele kaks tĂ€rni rakendamise keerukuse tĂ”ttu, lihtsalt seetĂ”ttu, et ma ei armasta tegeleda eskaleerimise, pikkuse muutmise jms. Jah, see vĂ”ib olla kallutatud, kuid koodi pean kirjutama mina — miks sundida end tegema seda, mis ei meeldiv.

KokkuvĂ”te: valime salvestamisvariandi, kus kasutatakse ahelat „pealkiri muutujapikkusega andmete jaoks“, kuna see on tĂ”hus ja lihtne rakendada.

BitivÀljade kasutamine kirjutamise operatsioonide eduka tulemuse jÀlgimiseks

Ma ei mÀleta enam, kust ma selle idee sain, kuid see nÀeb vÀlja enam-vÀhem nii:
Iga kirje jaoks eraldame mitu bitti lipu salvestamiseks.
KĂ€esoleva rÀÀgitud juhendiga tĂ€idetakse kĂ”ik bitid pĂ€rast erase'it 1'ga, ja me saame muuta 1 0'ks, kuid mitte vastupidi. Seega kasutame „lipp pole seadistatud“ jaoks 1 ja „lipp seadistatud“ jaoks 0.

Nii vÔib vÀlja nÀha muutuva pikkusega kirje salvestamine flash-mÀlus:

  1. Seame lipu „kirje pikkuse salvestamine algas“;
  2. Salvestame pikkuse;
  3. Seame lipu „andmete salvestamine algas“;
  4. Salvestame andmed;
  5. Seame lipu „salvestamine lĂ”ppes“.

Lisaks on meil lipp „tĂ”rge toimus“, kokku 4 bitilippu.

Sel juhul on meil kaks stabiilset olekut „1111“ — kirjutamine ei ole alanud ja „1000“ — kirjutamine lĂ€ks edukalt; ettenĂ€gematu katkestuse korral saame vahepealsed olekud, mida saame hiljem tuvastada ja töödelda.

LĂ€htekoht on huvitav, kuid see kaitseb ainult ootamatute toitekatkestuste ja sarnaste rike eest, mis on loomulikult oluline, kuid see ei ole sugugi ainus (ja isegi mitte peamine) vĂ”imalike rikke pĂ”hjuste rĂŒhm.

KokkuvÔte: Liigume edasi head lahendust otsides.

Kontrollsummad

Kontrollsummad annavad samuti vÔimaluse veenduda (piisava tÔenÀosusega) selles, et loeme tÀpselt seda, mis oli kirjutatud. Ja erinevalt eespool kÀsitletud bitivÀljadest töötavad need alati.

Kui vaatlen eeltoodud vĂ”imalike probleemiallikate loetelu, siis kontrollsummad suudavad tĂ”deda viga, sĂ”ltumata selle allikast. (v.a. vĂ”ib-olla pahatahtlikud tulnukad — nemad vĂ”ivad ka kontrollsummasid vĂ”ltsida).

Nii et kui meie eesmÀrk on kontrollida, et andmed on terved, on kontrollsummad suurepÀrane idee.

Kontrollsummade arvutamise algoritmi valik ei tekitanud kĂŒsimusi — CRC. Ühelt poolt vĂ”imaldavad matemaatilised omadused 100%-liselt tuvastada teatud tĂŒĂŒpi vigu, teisest kĂŒljest nĂ€itab see algoritm juhuslike andmete puhul tavaliselt kokkulangevuste tĂ”enĂ€osust, mis ei ole oluliselt kĂ”rgem teoreetilisest piirist. Minu NOR flash'i ringbuferi rakendus. Kuigi see ei ole kĂ”ige kiirem algoritm ega alati minimaalne kokkulangevuste arvu poolest, on sellel ĂŒks vĂ€ga oluline omadus: testides, millega ma olen kokku puutunud, ei ole olnud mustreid, kus see selgelt ebaĂ”nnestuks. Stabiilsus on antud juhul kĂ”ige olulisem omadus.

Mahuka uuringu nÀide: osa 1, osa 2 (linkid narod.ru, vabandust).

Siiski ei ole kontrollsumma valimise ĂŒlesanne veel lahendatud, CRC on terve kontrollsummade perekond. Tuleb otsustada pikkuse ĂŒle ja seejĂ€rel valida polĂŒĂŒm.

Kontrollsumma pikkuse valik ei ole nii lihtne kĂŒsimus, nagu see esmapilgul paistab.

Ilustreerin:
Oletame, et meil on vigade tÔenÀosus igas baitis Minu NOR flash'i ringbuferi rakendus ja ideaalne kontrollsumma, arvutame keskmise vigade arvu miljoni kirje kohta:

Andmed, bait
Kontrollsumma, bait
Unustatud vead
Valevea tuvastamised
Vale valeiduste kogus

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

Tundub lihtne – vali kontrollsummade pikkus vastavalt kaitstava teabe pikkusele, hoides valeandmete arvu miinimumis, ja asi on klaar.

Kuid lĂŒhikeste kontrollsummadega on probleem: need tuvastavad kĂŒll ĂŒksikuid bitivigu hĂ€sti, kuid vĂ”ivad suure tĂ”enĂ€osusega aktsepteerida tĂ€iesti suvalisi andmeid Ă”igetena. HabrĂ© oli juba artikel, mis kĂ€sitles probleemi reaalses elus.

SeetĂ”ttu, et kontrollsummade juhuslikku kokkulangemist praktiliselt vĂ€ltida, tuleb kasutada vĂ€hemalt 32-bitiseid kontrollsummasid (pikkuste jaoks ĂŒle 64 bitti kasutatakse tavaliselt krĂŒptograafilisi rĂ€sifunktsioone).

Kuigi olen varem öelnud, et ruumi tuleb hoida igas vĂ”imalikus viisis, kasutame siiski 32-bitist kontrollsummat (16 biti on liiga vĂ€he, kokkulanguse tĂ”enĂ€osus on ĂŒle 0,01%; ja 24 bitti, nagu öeldakse, ei ole ei siia ega sinna).

Siin vĂ”ib tekkida vastuvĂ€ide: kas me ikka sÀÀstsime iga bait kompresseerimise valimisel, et nĂŒĂŒd kohe 4 baidi jagu andmeid anda? Kas ei oleks parem mitte kompresseerida ja mitte lisada kontrollsummat? Loomulikult mitte, kompressiooni puudumine ei tĂ€henda, et me ei vaja terviklikkuse kontrolli.

PolĂŒnoomi valikul ei hakka me ratast uuesti leiutama, vaid vĂ”tame praegu populaarse CRC-32C.
See kood tuvastab 6 bitiviga pakettide puhul, mille pikkus on kuni 22 baiti (ilmselt kĂ”ige levinum juhtum meie jaoks), 4 bitiviga pakettide puhul pikkusega kuni 655 baiti (ka ĂŒsna tavaline meie seas), 2 vĂ”i mis tahes paaritu arvu bitiviga pakettide puhul suvalise mĂ”istliku pikkusega.

Kui kedagi huvitavad ĂŒksikasjad

Wikipediast leiate artikkli CRC-st.

CRC-32C koodiparameetrid jĂ€rgnevaga Kopmani saidil – ilmselt maailma juhtiv ekspert CRC alal.

V tema artiklis has veel ĂŒks huvitav kood, mis tagab pisut paremad parameetrid meie jaoks asjakohaste pikkade pakettide jaoks, kuid ma ei pea vahet tĂ€htsaks ja arvan end piisavalt pĂ€devaks, et valida kohandatud kood standardse ja hĂ€sti uuritud koodi asemel.

Veelgi enam, kuna meie andmed on kokku pressitud, tekib kĂŒsimus: arvutada kontrollsumma pressitud vĂ”i dekompressitud andmete pĂ”hjal?

Argumentid, miks kasutada kontrollsummat tihendamata andmete puhul:

  • me peame lĂ”puks kontrollima andmete sĂ€ilitamise korrektsust — just seda me otse kontrollime (samuti kontrollitakse vĂ”imalikke vigu tihendamise/dekompressiooni teostuses, kahjustusi, mis on pĂ”hjustatud samuti mĂ€lu vigadest jne);
  • algoritm deflate zlib'is on piisavalt kĂŒps ja ei tohiks kukku, kui sisendandmed on «vales» vormis; rohkemgi veel, sageli suudab ta iseseisvalt tuvastada sisendvoos esinevad vead, vĂ€hendades seelĂ€bi ĂŒleĂŒldist vea avastamise tĂ”enĂ€osust (ma tegin testi ĂŒhes bitti pöörates lĂŒhikeses kirjes, zlib tuvastas vea umbes kolmandikul juhtudest).

Argumentid, miks mitte kasutada kontrollsummat tihendamata andmete puhul:

  • CRC on loodud just vĂ€heste bittide vigade jaoks, mis on iseloomulikud flash-mĂ€lule (bittide viga tihendatud voos vĂ”ib pĂ”hjustada massilist muutumist vĂ€ljundvoos, kus me teoreetiliselt saame «kinni pĂŒĂŒda» kokkulangevuse);
  • mulle ei meeldi see mĂ”te, et anda dekompressorile potentsiaalselt vigaseid andmeid, kes teab, kuidas ta reageerib.

Selles projektis otsustasin ma kĂ”rvale hiilida ĂŒldiselt aktsepteeritud praktikast, mille kohaselt sĂ€ilitatakse kontrollsummad kokku surutud andmete suhtes.

KokkuvÔte: kasutame CRC-32C, kontrollsummat arvutame andmete pÔhjal sellises vormis, nagu need kirjutatakse flash-mÀele (pÀrast tihendamist).

Üksikasjalikkus

Liigne kodeerimine ei suuda muidugi tÀielikult vÀlistada andmete kadumise, kuid see vÔib oluliselt (tihti palju korda) vÀhendada taastamatute andmekao tÔenÀosust.

Saame kasutada erinevaid liigsuse vorme, et vigu parandada.
Hammingi koodid suudavad parandada ĂŒksikute bitivigu, Reed-Solomoni koodid on sĂŒmboolsed, mitmed andmekoopiad koos kontrollsummadega vĂ”i sellised kodeerimised nagu RAID-6 vĂ”ivad aidata andmeid taastada isegi massiliste kahjustuste korral.
Alguses olin ma kaldunud kasutama laialdaselt vigadele vastupanuvates kodeeringutes, kuid siis sain aru, et kÔigepealt tuleb mÔista, milliste vigade eest me tahame end kaitsta, ja seejÀrel valida kodeerimine.

Oleme varem rÀÀkinud, et vigu tuleb tuvastada vÔimalikult kiiresti. Millistel hetkedel vÔime vigadega silmitsi seista?

  1. Katkestatud salvestus (mingil pÔhjusel katkestas toiteallikas salvestamise, Raspberry hangus, ...)
    Kahjuks ei jÀÀ sellise vea korral muud ĂŒle, kui eirata mittesobivaid salvestusi ja pidada andmeid kadunuks;
  2. Salvestusvead (mingil pÔhjusel salvestati vÀlkmÀlusse mitte see, mis oli salvestatud)
    Selliseid vigu saame kohe tuvastada, kui teeme pÀrast salvestamist kontrollimise;
  3. Andmete moonutamine mÀlus sÀilitamise kÀigus;
  4. Lugemisvead
    Vea parandamiseks piisab juhtumite korral, kus kontrollsummad ei kattu, lugemise kordamisest mitu korda.

See tĂ€hendab, et ainult kolmanda tĂŒĂŒbi vead (andmete juhuslik riknemine sĂ€ilitamise ajal) ei saa olla parandatud ilma vigadele vastupidava kodeerimiseta. Tundub, et sellised vead on siiski ÀÀrmiselt ebatĂ”enĂ€olised.

KokkuvĂ”te: Otsustati loobuda liigsest kodeerimisest, kuid kui kasutus tĂ”estab, et see otsus on vale, naeratame selle kĂŒsimuse juurde tagasi (koos juba kogutud statistika rikkumistest, mis vĂ”imaldab valida parima kodeerimise variandi).

Muud

Muidugi ei vĂ”imalda artikli formaat pĂ”hjendada iga bitti formaadis. (ja mul on juba jĂ”ud otsas), seega lĂ€hen kiiresti ĂŒle mĂ”ningatele punktidele, millest varem ei rÀÀgitud.

  • Otsustasin teha kĂ”ik lehed „vĂ”rdsed“
    See tĂ€hendab, et ei tule mingisuguseid spetsiaalseid lehti metaandmete, eraldiseisvate voogude jne jaoks; selle asemel on ĂŒksainus voog, mis kirjutab kĂ”ik lehed jĂ€rjest.
    See tagab lehtede ĂŒhtlase kulumise, puudumise ainsast rikkepunktist ning lihtsalt meeldib;
  • Kindlasti tuleb arvestada formaadi versioonidega.
    Formaadi versiooninumbrita pÀises on kurjast!
    Piisab, kui lisada lehe pÀisesse vÀli koos mingi Magic Number'iga (signatuur), mis nÀitab kasutatavat formaadi versiooni (ma ei arva, et neid praktiliselt rohkem kui tosin oleks);
  • Kasutada kirjete jaoks (mida on vĂ€ga palju) muutliku pikkusega pealkirju, pĂŒĂŒdes enamikus juhtudest hoida nende pikkus 1 bait;
  • Pealkirja pikkuse ja kĂ€rpimise osa pikkuse kodeerimiseks kasutada muutliku pikkusega binaarkoodisid.

Oli vĂ€ga abiks veebigeneraator Huffman’i koode. Ainus minutitega suutsime leida vajalikud muutliku pikkusega koodid.

Andmete salvestamise formaadi kirjeldus

Baiti jÀrjekord

VĂ€ljad, mille suurus ĂŒletab ĂŒhe bait, salvestatakse big-endian formaadis (vĂ”rgubaiti jĂ€rjekord), st 0x1234 salvestatakse kui 0x12, 0x34.

LehekĂŒlgede jagamine

KÔik vÀlkmÀlu on jagatud vÔrdse suurusega lehekesteks.

Lehekese vaikimisi suurus on 32KB, kuid mitte rohkem kui 1/4 kogu mÀlukiibi suurusest (4MB kiibi puhul on see 128 lehekest).

Iga lehekĂŒlg salvestab andmeid sĂ”ltumatult teistest (tĂŒhikud ĂŒhe lehekĂŒlje andmed ei viita teistele lehekĂŒlgede andmetele).

KĂ”ik lehekĂŒljed on nummerdatud loomulikus jĂ€rjekorras (aadresside tĂ”usvas jĂ€rjekorras), alustades numbrist 0 (null lehekĂŒlg algab aadressilt 0, esimene 32KB-lt, teine 64KB-lt jne).

MĂ€lukiipi kasutatakse tsĂŒklilise pubufferina (ringbuffer), st alguses salvestamine toimub lehekĂŒljel numbriga 0, seejĂ€rel numbriga 1, 
 kui tĂ€idame viimase lehekĂŒlje, algab uus tsĂŒkkel ja salvestamine jĂ€tkub null lehekĂŒljelt.

LehekĂŒlje sees

Minu NOR flash'i ringbuferi rakendus
LehekĂŒlje alguses on 4-baitine lehekĂŒlje pealkiri, seejĂ€rel pealkirja kontrollsummat (CRC-32C), edasi on salvestatud kirjed formaadis 'pealkiri, andmed, kontrollsumma'.

Lehe pealkiri (roheline-baar) koosneb:

  • kahebytesest Magic Number (see on ka formaadi versiooni tĂ€his)
    praeguse formaadi versioon on defineeritud kui 0xed00 ⊕ lehe number;
  • kahebytesest arvestit 'Lehe versioon' (mĂ€lu kirjutustsĂŒkli number).

Lehe kirjeldused on salvestatud kompressioonivormingus (kasutatakse deflate algoritmi). KĂ”ik kirjeldused ĂŒhel lehel kompressitakse ĂŒhes voos (kasutatakse ĂŒhte ĂŒldist sĂ”nastikku), iga uue lehe puhul algab kompressioon uuesti. Seega, et dekompressida mistahes kirje, on vajalikud kĂ”ik varasemad kirjed sellest lehelt (ja ainult sellest).

Iga kirje kompressitakse Z_SYNC_FLUSH lipuga, mille tulemusena kompressioonivoo lĂ”ppu satub 4 baiti 0x00, 0x00, 0xff, 0xff, millele vĂ”ivad eelnevalt jĂ€rgneb ĂŒhe vĂ”i kahe null-bait.
See jÀrjekord (pikkusega 4, 5 vÔi 6 bitti) eemaldatakse meeldejÀtmise ajal.

Kirje pealkiri koosneb 1, 2 vÔi 3 baitist, mis sisaldavad:

  • ĂŒht bitti (T), mis nĂ€itab kirje tĂŒĂŒpi: 0 — kontekst, 1 — ajakiri;
  • muutuva pikkusega vĂ€li (S) vahemikus 1 kuni 7 bitti, mis mÀÀrab pealkirja pikkuse ja 'saba', mille tuleb paigaldada kirjele dekompressimiseks;
  • kirje pikkus (L).

S vÀÀrtuste tabel:

S
Pealkirja pikkus, baitides
Kirjutamisel jÀetakse kÔrvale, baitides

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)

PĂŒĂŒdsin illustreerida, ei tea, kui selgelt see vĂ€lja tuli:
Minu NOR flash'i ringbuferi rakendus
Kollasega on tÀhistatud T vÀli, valgega S vÀli, rohelisega L (kompressitud andmete pikkus baitides), sinisega kompressitud andmed, punasega lÔppbaitide kompressitud andmetest, mida ei kirjutata vÀlkmÀllu.

Seega saame enim levinud pikkusega kirjeid (kuni 63+5 baiti kompressitud kujul) kirjutada ĂŒhe baitiga.

Iga kirje jÀrel hoitakse CRC-32C kontrollsummat, mille algvÀÀrtusena (init) kasutatakse eelneva kontrollsummat pöördvÀÀrtust.

CRC-l on omadus «kestvus», see töötab (pluss-miinus bitide pööramine protsessi kÀigus) jÀrgmiseks valemiks: Minu NOR flash'i ringbuferi rakendus.
Ehk tegelikult arvutame CRC kÔigist eelnevatest baitidest pealkirjades ja andmetes sellel lehel.

Otse kontrollsummast jÀrgneb jÀrgmise kirje pealkiri.

Pealkiri on konstrueeritud nii, et selle esimene bait oleks alati erinev 0x00-st ja 0xff-st (kui pealkirja esimene bait on 0xff, tÀhendab see, et see on praegu kasutamata ala; 0x00 signaalib viga).

Umbes algoritmid

Lugemine flash-mÀlust

Iga lugemine toimub kontrollsummaga.
Kui kontrollsumma ei ĂŒhti – loetakse andmeid mitu korda, lootes siiski Ă”igeid andmeid lugeda.

(see on mÔttekas, Linux ei mÀÀra NOR Flash'i lugemise vahemÀlu, on tÔestatud)

Kirjutamine flash-mÀllu

Kirjutame andmed.
Lugeme need.

Kui loetud andmed ei ĂŒhti kirjutatud andmetega – tĂ€idame ala nullidega ja signaalime vea.

Uue mikroskeemi tööks ettevalmistamine

Initsialiseerimiseks kirjutatakse esimese (tegelikult nullenda) lehekĂŒlje algusse pealkiri versiooniga 1.
PĂ€rast seda kirjutatakse sellesse lehekĂŒlge algne kontekst (sisaldab automaadi UUID-d ja vaikeseadeid).

KÔik, flash-mÀlu on tööks valmis.

Automaatlaadimine

Laadimise ajal loetakse iga lehe esimesed 8 baiti (pealkiri + CRC), lehekĂŒljed, millel on tundmatu Magic Number vĂ”i vale CRC, jĂ€etakse tĂ€helepanuta.
Valitud „Ôigest” lehekĂŒljest valitakse lehekĂŒljed, mille versioon on maksimaalne, ning sealt vĂ”etakse lehekĂŒlg, mille number on kĂ”ige suurem.
Loeme esimese kirje, kontrollime CRC korrektsetsust, konteksti lipu olemasolu. Kui kĂ”ik on korras, loeme selle lehe praeguseks. Kui ei, siis pöördume tagasi eelmisele lehele, kuni leiame „elava” lehe.
Leitud lehelt loeme kÔik kirjed, need, millel on konteksti lipp, rakendame.
Salvestame zlib sÔnastiku (see on vajalik selle lehe tÀiendamiseks).

KĂ”ik, laadimine on lĂ”petatud, kontekst on taastatud, nĂŒĂŒd saab töötada.

Kirje lisamine ajakirja

Surume kirje kokku Ă”igete sĂ”nastikuga, kasutades Z_SYNC_FLUSH. Kontrollime, kas kokkusurutud kirje mahubćœ“ć‰é .
Kui see ei mahutu (vÔi lehel olid CRC vead), alustame uut lehte (vt allpool).
Kandke kirje ja CRC. Kui juhtub viga, alustame uut lehte.

Uus leht

Valime vabade lehtede hulgast vÀikseima numbri (vaba leht on see, mille pealkirjas on vale kontrollsumm ja mille versioon on vÀiksem kui praegune). Kui selliseid lehti pole, valime lehe, mille number on vÀikseim nendest, mille versioon on sama mis praegune.
Teeme valitud lehe erase. Kontrollime sisu 0xff-ga. Kui midagi on valesti, vÔtame jÀrgmise vaba lehe jne.
Kustutatud lehe peale kirjutame pealkirja, esimeseks kirje on praegune konteksti seisund ja jÀrgmiseks - kui see olemas, siis kirjutamata logikiri.

Formaatide rakendatavus

Minu arvates on see hea formaat igasuguste rohkem-vÀhem kokkutÔmmatavate info voogude (lihtne tekst, JSON, MessagePack, CBOR, vÔib-olla protobuf) talletamiseks NOR Flashis.

TÔepoolest, formaat on kohandatud SLC NOR Flashi jaoks.

Seda ei tasu kasutada kĂ”rge BER-iga meediate peal, nagu NAND vĂ”i MLC NOR. (Kas selline mĂ€lu on ĂŒldse mĂŒĂŒgil? Olen kohanud viiteid ainult paranduskoode kĂ€sitlevates töödes.).

Veel enam, seda ei tasu kasutada seadmete puhul, millel on oma FTL: USB flash, SD, MicroSD jne. (sellise mĂ€lu jaoks tegin lehe formaadi 512 baitiga, lehtede alguses oleva allkirja ja ainulaadsete kirje numbritega — mĂ”nikord suutsin „kinnise” mĂ€lupulga lihtsa jĂ€rjestikuse lugemisega kĂ”ik andmed taastada).

SĂ”ltuvalt ĂŒlesannetest saab formaati ilma muudatusteta kasutada mĂ€lupulkadel alates 128 Kb (16 Kb) kuni 1 Gb (128 Mb). Soovi korral saab seda kasutada ka suuremate mahutavustega kiipidel, kuid tĂ”enĂ€oliselt tuleb kohandada lehe suurust. (Aga siin tuleb juba mĂ€ngu majanduslik otstarbekus, NOR Flash suure mahutavusega hind ei rÔÔmusta).

Kui keegi leiab formaadi huvitavaks ja soovib seda kasutada avatud projektis — kirjutage, pĂŒĂŒan leida aega koodi korrastamiseks ja githubi ĂŒleslaadimiseks.

KokkuvÔte

Nagu nÀha, on formaadist lÔpuks saanud lihtne ja isegi igav.

Artiklis on keeruline peegeldada oma vaatepunkti evolutsiooni, kuid uskuge: algselt tahtsin luua midagi keerulist, purunematut, mis suudaks ellu jÀÀda isegi tuumaplahvatuse lÀheduses. Kuid mÔistus (loodan) vÔitis ja jÀrk-jÀrgult nihkusid prioriteedid lihtsuse ja kompaktsuse suunas.

Kas on vÔimalik, et ma eksisin? Jah, muidugi. On tÀiesti vÔimalik, et oleme ostnud partiid ebakvaliteetseid mikroskeeme. VÔi mÔnel muul pÔhjusel ei vasta seadmed usaldusvÀÀrsuse ootustele.

Kas mul on selleks juhtumiks plaan? Ma arvan, et artiklit lugedes ei kahtle sa, et plaan on olemas. Ja isegi mitte ĂŒks.

Kui öelda tÔsisemalt, siis formaat on loodud samal ajal nii tööversiooniks kui ka katsetuseks.

Praegu töötab laual kÔik normaalselt, sÔna otseses mÔttes paar pÀeva tagasi alustatakse lahenduse rakendamist. (umbes) sajale seadmele, vaatame, mis juhtub "pÀris" kasutuses (Ônneks, loodan, et formaat vÔimaldab usaldusvÀÀrselt tuvastada tÔrkeid; nii et saame koguda pÔhjalikku statistikat). MÔne kuu pÀrast saab jÀreldusi teha. (ja kui ei vea, siis ka varem).

Kui kasutamise tulemusena tuvastatakse tÔsised probleemid ja on vaja tÀiendusi, siis kindlasti kirjutan sellest.

Kirjandus

Ei tahtnud koostada pikka tĂŒĂŒtut loetelu kasutatud töödest, lĂ”puks on kĂ”igil ju Google olemas.

Siin otsustasin jĂ€tta nimekirja leidudest, mis tundusid mulle eriti huvitavad, kuid jĂ€rk-jĂ€rgult on need otseselt artikli teksti ĂŒle kandunud ning nimekirjas on jÀÀnud vaid ĂŒks punkt:

  1. Tööriist infgen autorilt zlib. Suudab selgelt kuvada deflate/zlib/gzip arhiivide sisu. Kui peate tegelema deflate (vÔi gzip) formaadi sisemise struktuuri mÔistmisega, siis soovitan tungivalt.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster