Mana gredzena bufera ievieÅ”ana NOR zibspuldzē

Aizvēsture

Ir mÅ«su paÅ”u dizaina tirdzniecÄ«bas automāti. Raspberry Pi iekÅ”pusē un daži vadi uz atseviŔķas plates. Ir pieslēgts monētu pieņēmējs, vekseļu akceptors, bankas terminālis... Visu vada paÅ”u rakstÄ«ta programma. Visa darba vēsture tiek ierakstÄ«ta žurnālā zibatmiņas diskā (MicroSD), kas pēc tam tiek pārsÅ«tÄ«ta caur internetu (izmantojot USB modemu) uz serveri, kur tā tiek saglabāta datu bāzē. PārdoÅ”anas informācija tiek ielādēta 1c, ir arÄ« vienkārÅ”s tÄ«mekļa interfeiss uzraudzÄ«bai utt.

Tas ir, žurnāls ir vitāli svarÄ«gs - grāmatvedÄ«bai (ieņēmumi, pārdoÅ”ana utt.), UzraudzÄ«bai (visa veida neveiksmes un citi nepārvaramas varas apstākļi); Tā, varētu teikt, ir visa informācija, kas mums ir par Å”o iekārtu.

problēma

Zibatmiņas diski parāda sevi kā ļoti neuzticamas ierÄ«ces. Viņiem neizdodas ar apskaužamu regularitāti. Tas noved gan pie maŔīnas dÄ«kstāves, gan (ja kāda iemesla dēļ žurnālu nevarēja pārsÅ«tÄ«t tieÅ”saistē) pie datu zuduma.

Å Ä« nav pirmā pieredze ar zibatmiņu lietoÅ”anu, pirms tam bija vēl viens projekts ar vairāk nekā simts ierÄ«cēm, kur žurnāls tika glabāts USB zibatmiņās, bija arÄ« problēmas ar uzticamÄ«bu, brīžiem to skaits, kas neizdevās mēnesis bija desmitos. Mēs izmēģinājām dažādus zibatmiņas diskus, tostarp zÄ«molus ar SLC atmiņu, un daži modeļi ir uzticamāki nekā citi, taču zibatmiņas disku nomaiņa problēmu radikāli neatrisināja.

UzmanÄ«bu! Ilgi lasÄ«ts! Ja jÅ«s neinteresē ā€œkāpēcā€, bet tikai ā€œkāā€, varat iet taisni Beigās raksti.

Å Ä·Ä«dums

Pirmā lieta, kas nāk prātā, ir: atsakieties no MicroSD, instalējiet, piemēram, SSD un sāknējiet no tā. Teorētiski iespējams, iespējams, bet salÄ«dzinoÅ”i dārgs un ne tik uzticams (pievienots USB-SATA adapteris; budžeta SSD kļūmju statistika arÄ« nav iepriecinoÅ”a).

USB HDD arī neizskatās īpaŔi pievilcīgs risinājums.

Tāpēc mēs nonācām pie Ŕādas iespējas: atstājiet sāknÄ“Å”anu no MicroSD, bet izmantojiet tos tikai lasÄ«Å”anas režīmā un saglabājiet operāciju žurnālu (un citu informāciju, kas ir unikāla konkrētai aparatÅ«ras daļai - sērijas numuru, sensoru kalibrÄ“Å”anu utt.) kaut kur citur. .

Tēma par tikai lasāmu FS avenēm jau ir pētÄ«ta gan iekÅ”pusē, gan ārpusē, Å”ajā rakstā es nekavÄ“Å”os pie ievieÅ”anas detaļām (bet ja bÅ«s interese, varbÅ«t uztaisÄ«Å”u kādu mini rakstu par Å”o tēmu). VienÄ«gais, ko es vēlētos atzÄ«mēt, ir tas, ka gan no personÄ«gās pieredzes, gan no to lietotāju atsauksmēm, kuri to jau ir ieviesuÅ”i, ir palielināta uzticamÄ«ba. Jā, nav iespējams pilnÄ«bā atbrÄ«voties no bojājumiem, taču ir pilnÄ«gi iespējams ievērojami samazināt to biežumu. Un kartes kļūst vienotas, kas ievērojami atvieglo nomaiņu apkalpojoÅ”ajam personālam.

Aparatūra

ÄŖpaÅ”u Å”aubu par atmiņas veida izvēli - NOR Flash - nebija.
Argumenti:

  • vienkārÅ”s savienojums (visbiežāk SPI kopne, kuras lietoÅ”anā jau ir pieredze, tāpēc aparatÅ«ras problēmas nav paredzētas);
  • smieklÄ«ga cena;
  • standarta darbÄ«bas protokols (ievieÅ”ana jau ir Linux kodolā, ja vēlaties, varat paņemt treŔās puses, kas arÄ« ir klāt, vai pat uzrakstÄ«t savu, par laimi viss ir vienkārÅ”i);
  • uzticamÄ«ba un resursi:
    no tipiskas datu lapas: dati tiek glabāti 20 gadus, 100000 XNUMX dzÄ“Å”anas ciklu katram blokam;
    no treÅ”o puÅ”u avotiem: ārkārtÄ«gi zems BER, postulē, ka nav nepiecieÅ”ami kļūdu laboÅ”anas kodi (dažos darbos ECC tiek uzskatÄ«ts par NOR, bet parasti tie joprojām nozÄ«mē MLC NOR; tas arÄ« notiek).

Novērtēsim prasības apjomam un resursiem.

Es vēlētos, lai dati tiktu garantēti saglabāti vairākas dienas. Tas nepiecieÅ”ams, lai jebkādu komunikācijas problēmu gadÄ«jumā netiktu zaudēta pārdoÅ”anas vēsture. Å ajā periodā mēs koncentrēsimies uz 5 dienām (pat ņemot vērā nedēļas nogales un svētku dienas) problēmu var atrisināt.

Å obrÄ«d savācam ap 100kb žurnālu dienā (3-4 tÅ«kstoÅ”i ierakstu), bet pamazām Å”is skaitlis aug - pieaug detalizācija, tiek pievienoti jauni notikumi. Turklāt dažreiz ir pārrāvumi (piemēram, daži sensori sāk surogātpasta ziņojumus ar viltus pozitÄ«viem rezultātiem). Mēs aprēķināsim 10 tÅ«kstoÅ”iem ierakstu pa 100 baiti katram - megabaiti dienā.

Kopumā iznāk 5 MB tÄ«ru (labi saspiestu) datu. Vairāk viņiem (aptuvens aprēķins) 1 MB pakalpojumu datu.

Tas nozÄ«mē, ka mums ir nepiecieÅ”ama 8 MB mikroshēma, ja neizmantojam saspieÅ”anu, vai 4 MB, ja to izmantojam. Diezgan reāli skaitļi Ŕāda veida atmiņai.

Kas attiecas uz resursu: ja plānojam, ka visa atmiņa tiks pārrakstÄ«ta ne biežāk kā reizi 5 dienās, tad 10 gadu kalpoÅ”anas laikā mēs saņemam mazāk par tÅ«kstoti pārrakstÄ«Å”anas ciklu.
AtgādināŔu, ka ražotājs sola simts tūkstoŔus.

Mazliet par NOR vs NAND

MÅ«sdienās, protams, NAND atmiņa ir daudz populārāka, taču es to neizmantotu Å”im projektam: NAND, atŔķirÄ«bā no NOR, obligāti pieprasa kļūdu laboÅ”anas kodus, sliktu bloku tabulu utt., kā arÄ« kājas NAND mikroshēmas parasti ir daudz vairāk.

NOR trūkumi ietver:

  • mazs apjoms (un attiecÄ«gi augsta cena par megabaitu);
  • zems sakaru ātrums (lielākoties tāpēc, ka tiek izmantots seriālais interfeiss, parasti SPI vai I2C);
  • lēna dzÄ“Å”ana (atkarÄ«bā no bloka lieluma tas aizņem no sekundes daļas lÄ«dz vairākām sekundēm).

Šķiet, ka mums nekā kritiska nav, tāpēc turpinām.

Ja detaļas ir interesantas, mikroshēma ir izvēlēta at25df321a (tomēr tas nav svarīgi, tirgū ir daudz analogu, kas ir savietojams ar pinout un komandu sistēmu; pat ja mēs vēlamies instalēt cita ražotāja un/vai cita izmēra mikroshēmu, viss darbosies, nemainot kods).

Es izmantoju Linux kodolā iebÅ«vēto draiveri, Raspberry, pateicoties ierīču koka pārklājuma atbalstam, viss ir ļoti vienkārÅ”i - jums ir jāievieto kompilētais pārklājums /boot/overlays un nedaudz jāpārveido /boot/config.txt.

Dts faila piemērs

Godīgi sakot, es neesmu pārliecināts, ka tas ir uzrakstīts bez kļūdām, bet tas darbojas.

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

Un vēl viena rindiņa failā config.txt

dtoverlay=at25:spimaxfrequency=50000000

Es izlaidÄ«Å”u aprakstu par mikroshēmas savienoÅ”anu ar Raspberry Pi. No vienas puses, es neesmu elektronikas eksperts, no otras puses, viss Å”eit ir banāls pat man: mikroshēmai ir tikai 8 kājas, no kurām mums ir nepiecieÅ”ams zemējums, jauda, ā€‹ā€‹SPI (CS, SI, SO, SCK ); lÄ«meņi ir tādi paÅ”i kā Raspberry Pi, nav nepiecieÅ”ama papildu elektroinstalācija - vienkārÅ”i pievienojiet norādÄ«tos 6 kontaktus.

Problēmas paziņojums

Kā parasti, problēmas paziņojums iziet cauri vairākām iterācijām, un man Ŕķiet, ka ir pienācis laiks nākamajam. Tāpēc apstāsimies, saliksim kopā jau uzrakstÄ«to un precizēsim detaļas, kas paliek ēnā.

Tātad, mēs esam nolēmuÅ”i, ka žurnāls tiks saglabāts SPI NOR Flash.

Kas ir NOR Flash tiem, kas nezina?

Šī ir nemainīga atmiņa, ar kuru varat veikt trīs darbības:

  1. LasīŔana:
    VisizplatÄ«tākais lasÄ«jums: mēs pārsÅ«tām adresi un nolasām tik daudz baitu, cik mums nepiecieÅ”ams;
  2. Ieraksts:
    RakstÄ«Å”ana uz NOR zibspuldzi izskatās kā parasta, taču tai ir viena Ä«patnÄ«ba: jÅ«s varat mainÄ«t tikai no 1 uz 0, bet ne otrādi. Piemēram, ja mums atmiņas Ŕūnā bija 0x55, tad pēc 0x0f ierakstÄ«Å”anas tajā jau tiks saglabāts 0x05 (skatiet tabulu tieÅ”i zemāk);
  3. Dzēst:
    Protams, mums ir jāspēj veikt pretēju darbÄ«bu - mainiet 0 uz 1, tieÅ”i tam ir paredzēta dzÄ“Å”anas darbÄ«ba. AtŔķirÄ«bā no pirmajiem diviem, tas darbojas nevis ar baitiem, bet ar blokiem (minimālais dzÄ“Å”anas bloks izvēlētajā mikroshēmā ir 4kb). DzÄ“Å”ana iznÄ«cina visu bloku un ir vienÄ«gais veids, kā mainÄ«t 0 uz 1. Tāpēc, strādājot ar zibatmiņu, bieži ir jāsaskaņo datu struktÅ«ras ar dzÄ“Å”anas bloka robežu.
    Ieraksts NOR Flash:

Binārie dati

Bija
01010101

Ierakstīts
00001111

Kļuvis
00000101

Pats žurnāls ir mainÄ«ga garuma ierakstu secÄ«ba. Tipisks ieraksta garums ir aptuveni 30 baiti (lai gan dažkārt gadās ieraksti, kuru garums ir vairāki kilobaiti). Å ajā gadÄ«jumā mēs strādājam ar tiem vienkārÅ”i kā baitu kopu, bet, ja jÅ«s interesē, ierakstos tiek izmantots CBOR

Papildus žurnālam mums ir jāsaglabā zināma ā€œiestatÄ«jumaā€ informācija, gan atjaunināta, gan ne: noteikts ierÄ«ces ID, sensoru kalibrÄ“Å”ana, karodziņŔ ā€œierÄ«ce ir Ä«slaicÄ«gi atspējotaā€ utt.
Å Ä« informācija ir atslēgas vērtÄ«bu ierakstu kopa, kas tiek glabāta arÄ« CBOR. Mums nav daudz Ŕīs informācijas (ne vairāk kā daži kilobaiti), un tā tiek reti atjaunināta.
Tālāk mēs to sauksim par kontekstu.

Ja atceramies, kur Å”is raksts sākās, ļoti svarÄ«gi ir nodroÅ”ināt uzticamu datu uzglabāŔanu un, ja iespējams, nepārtrauktu darbÄ«bu pat aparatÅ«ras kļūmju/datu bojājumu gadÄ«jumā.

Kādus problēmu avotus var uzskatīt?

  • Izslēdziet rakstÄ«Å”anas/dzÄ“Å”anas darbÄ«bu laikā. Tas ir no kategorijas ā€œnav nekāda trika pret lauzniā€.
    Informācija no diskusijas stackexchange: kad strāva tiek izslēgta, strādājot ar zibspuldzi, gan dzÄ“Å”ana (iestatÄ«ts uz 1), gan rakstÄ«Å”ana (iestatÄ«ts uz 0) noved pie nedefinētas darbÄ«bas: datus var rakstÄ«t, daļēji rakstÄ«t (teiksim, mēs pārsÅ«tÄ«jām 10 baitus/80 bitus , bet vēl nevar ierakstÄ«t tikai 45 bitus), iespējams, ka daži biti bÅ«s ā€œstarpējāā€ stāvoklÄ« (lasot var iegÅ«t gan 0, gan 1);
  • Kļūdas paŔā zibatmiņā.
    BER, lai arī ļoti zems, nevar būt vienāds ar nulli;
  • Autobusu kļūdas
    Dati, kas tiek pārraidÄ«ti, izmantojot SPI, netiek nekādā veidā aizsargāti, var rasties gan viena bita kļūdas, gan sinhronizācijas kļūdas - bitu pazaudÄ“Å”ana vai ievietoÅ”ana (kas noved pie masveida datu kropļojumiem);
  • Citas kļūdas/kļūmes
    Kļūdas kodā, Raspberry kļūmes, citplanētieÅ”u iejaukÅ”anās...

Esmu formulējis prasÄ«bas, kuru izpilde, manuprāt, ir nepiecieÅ”ama uzticamÄ«bas nodroÅ”ināŔanai:

  • ierakstiem nekavējoties jāiet zibatmiņā, aizkavētie ieraksti netiek ņemti vērā; - ja rodas kļūda, tā ir jāatklāj un jāapstrādā pēc iespējas ātrāk; - sistēmai pēc iespējas jāatgÅ«stas no kļūdām.
    (piemērs no dzÄ«ves ā€œkā tam nevajadzētu bÅ«tā€, ar ko, manuprāt, ir nācies sastapties: pēc avārijas atsāknÄ“Å”anas failu sistēma ir ā€œsalauztaā€ un operētājsistēma neboot)

Idejas, pieejas, pārdomas

Kad sāku domāt par Å”o problēmu, manā galvā pavÄ«dēja daudzas idejas, piemēram:

  • izmantot datu kompresiju;
  • izmantot gudras datu struktÅ«ras, piemēram, ierakstu galvenes glabāt atseviŔķi no paÅ”iem ierakstiem, lai, ja kādā ierakstā ir kļūda, pārējos varētu nolasÄ«t bez problēmām;
  • izmantojiet bitu laukus, lai kontrolētu ierakstÄ«Å”anas pabeigÅ”anu, kad baroÅ”ana ir izslēgta;
  • uzglabāt kontrolsummas par visu;
  • izmantojiet kāda veida trokŔņu izturÄ«gu kodÄ“Å”anu.

Dažas no Ŕīm idejām tika izmantotas, bet citas tika nolemtas atteikties. Ejam kārtībā.

Datu saspieŔana

PaÅ”i notikumi, ko mēs ierakstām žurnālā, ir diezgan lÄ«dzÄ«gi un atkārtojami (ā€œiemeta 5 rubļu monētuā€, ā€œnospieda naudas doÅ”anas poguā€, ...). Tāpēc kompresijai vajadzētu bÅ«t diezgan efektÄ«vai.

Kompresijas slodze ir nenozÄ«mÄ«ga (mÅ«su procesors ir diezgan jaudÄ«gs, pat pirmajam Pi bija viens kodols ar frekvenci 700 MHz, paÅ”reizējiem modeļiem ir vairāki kodoli ar frekvenci virs gigaherciem), maiņas kurss ar krātuvi ir zems (vairāki megabaiti sekundē), ierakstu izmērs ir mazs. Kopumā, ja saspieÅ”ana ietekmē veiktspēju, tā bÅ«s tikai pozitÄ«va. (pilnÄ«gi nekritiski, tikai paziņoju). Turklāt mums nav Ä«sts iegultais, bet parasts Linux - tāpēc ievieÅ”anai nevajadzētu prasÄ«t daudz pūļu (pietiek tikai saistÄ«t bibliotēku un izmantot vairākas no tās funkcijas).

Žurnāla gabals tika izņemts no darba ierÄ«ces (1.7 MB, 70 tÅ«kstoÅ”i ierakstu) un vispirms tika pārbaudÄ«ta saspiežamÄ«ba, izmantojot datorā pieejamos gzip, lz4, lzop, bzip2, xz, zstd.

  • gzip, xz, zstd uzrādÄ«ja lÄ«dzÄ«gus rezultātus (40Kb).
    Biju pārsteigts, ka modīgais xz Ŕeit sevi parādīja gzip vai zstd līmenī;
  • lzip ar noklusējuma iestatÄ«jumiem sniedza nedaudz sliktākus rezultātus;
  • lz4 un lzop uzrādÄ«ja ne pārāk labus rezultātus (150Kb);
  • bzip2 uzrādÄ«ja pārsteidzoÅ”i labu rezultātu (18Kb).

Tātad dati ir ļoti labi saspiesti.
Tātad (ja neatradÄ«sim liktenÄ«gus trÅ«kumus) bÅ«s saspieÅ”ana! VienkārÅ”i tāpēc, ka tajā paŔā zibatmiņas diskā var ievietot vairāk datu.

Padomāsim par trūkumiem.

Pirmā problēma: mēs jau esam vienojuÅ”ies, ka katram ierakstam nekavējoties jāiet uz mirgoÅ”anu. Parasti arhivētājs apkopo datus no ievades straumes, lÄ«dz nolemj, ka ir pienācis laiks rakstÄ«t nedēļas nogalē. Mums nekavējoties jāsaņem saspiests datu bloks un jāsaglabā tas nemainÄ«gā atmiņā.

Es redzu trīs veidus:

  1. Saspiediet katru ierakstu, izmantojot vārdnīcas saspieŔanu, nevis iepriekŔ aprakstītos algoritmus.
    Tas ir pilnÄ«gi strādājoÅ”s variants, bet man tas nepatÄ«k. Lai nodroÅ”inātu vairāk vai mazāk pienācÄ«gu saspieÅ”anas lÄ«meni, vārdnÄ«cai jābÅ«t ā€œpielāgotaiā€ konkrētiem datiem; jebkuras izmaiņas novedÄ«s pie saspieÅ”anas lÄ«meņa katastrofālas pazemināŔanās. Jā, problēmu var atrisināt, izveidojot jaunu vārdnÄ«cas versiju, taču tas sagādā galvassāpes ā€“ mums bÅ«s jāsaglabā visas vārdnÄ«cas versijas; katrā ierakstā mums bÅ«s jānorāda, ar kuru vārdnÄ«cas versiju tā tika saspiesta...
  2. Saspiest katru ierakstu, izmantojot ā€œklasiskosā€ algoritmus, taču neatkarÄ«gi no citiem.
    Apskatāmie saspieÅ”anas algoritmi nav paredzēti darbam ar Ŕāda izmēra ierakstiem (desmitiem baitu), saspieÅ”anas pakāpe nepārprotami bÅ«s mazāka par 1 (tas ir, datu apjoma palielināŔana, nevis saspieÅ”ana);
  3. Pēc katra ieraksta veiciet FLUSH.
    Daudzas saspieÅ”anas bibliotēkas atbalsta FLUSH. Å Ä« ir komanda (vai saspieÅ”anas procedÅ«ras parametrs), pēc kuras saņemÅ”anas arhivētājs veido saspiestu straumi, lai to varētu izmantot atjaunoÅ”anai viss nesaspiesti dati, kas jau ir saņemti. Tāds analogs sync failu sistēmās vai commit SQL formātā.
    SvarÄ«gi ir tas, ka turpmākajās saspieÅ”anas operācijās varēs izmantot uzkrāto vārdnÄ«cu un saspieÅ”anas pakāpe necietÄ«s tik daudz kā iepriekŔējā versijā.

Es domāju, ka ir skaidrs, ka es izvēlējos treÅ”o iespēju, apskatÄ«sim to sÄ«kāk.

Atrasts lielisks raksts par FLUSH zlib.

Es veicu ceļgala pārbaudi, pamatojoties uz rakstu, paņēmu 70 tÅ«kstoÅ”us žurnāla ierakstu no reālas ierÄ«ces ar lapas izmēru 60Kb (pie lapas izmēra atgriezÄ«simies vēlāk) saņēma:

Neapstrādāti dati
SaspieŔana gzip -9 (bez FLUSH)
zlib ar Z_PARTIAL_FLUSH
zlib ar Z_SYNC_FLUSH

Apjoms, KB
1692
40
352
604

No pirmā acu uzmetiena FLUSH piedāvātā cena ir pārmērÄ«gi augsta, bet patiesÄ«bā mums ir maz izvēles - vai nu nesaspiest vispār, vai arÄ« saspiest (un ļoti efektÄ«vi) ar FLUSH. Mēs nedrÄ«kstam aizmirst, ka mums ir 70 tÅ«kstoÅ”i ierakstu, Z_PARTIAL_FLUSH ieviestā dublÄ“Å”ana ir tikai 4-5 baiti uz ierakstu. Un kompresijas pakāpe izrādÄ«jās gandrÄ«z 5: 1, kas ir vairāk nekā lielisks rezultāts.

Tas var būt pārsteigums, taču Z_SYNC_FLUSH patiesībā ir efektīvāks veids, kā veikt FLUSH

Izmantojot Z_SYNC_FLUSH, katra ieraksta pēdējie 4 baiti vienmēr bÅ«s 0x00, 0x00, 0xff, 0xff. Un, ja mēs tos zinām, tad mums tie nav jāuzglabā, tāpēc galÄ«gais izmērs ir tikai 324 Kb.

Rakstam, uz kuru es pievienoju saiti, ir paskaidrojums:

Tiek pievienots jauns 0 tipa bloks ar tukŔu saturu.

0 tipa bloks ar tukŔu saturu sastāv no:

  • trÄ«s bitu bloka galvene;
  • 0 lÄ«dz 7 biti, kas vienādi ar nulli, lai panāktu baitu izlÄ«dzināŔanu;
  • četru baitu secÄ«ba 00 00 FF FF.

Kā jÅ«s viegli varat redzēt, pēdējā blokā pirms Å”iem 4 baitiem ir no 3 lÄ«dz 10 nulles bitiem. Tomēr prakse ir parādÄ«jusi, ka patiesÄ«bā ir vismaz 10 nulles biti.

Izrādās, ka Ŕādi Ä«si datu bloki parasti (vienmēr?) tiek kodēti, izmantojot 1. tipa bloku (fiksēto bloku), kas obligāti beidzas ar 7 nulles bitiem, kopā dodot 10-17 garantētus nulles bitus (un pārējie bÅ«s bÅ«t nulle ar varbÅ«tÄ«bu aptuveni 50%).

Tātad testa datos 100% gadÄ«jumu pirms 0x00, 0x00, 0xff, 0xff ir viens nulles baits, un vairāk nekā treÅ”daļā gadÄ«jumu ir divi nulles baiti. (iespējams, fakts ir tāds, ka es izmantoju bināro CBOR, un, izmantojot teksta JSON, biežāk bÅ«tu 2. tipa bloki - dinamiskais bloks, attiecÄ«gi, tiktu sastapti bloki bez papildu nulles baitiem pirms 0x00, 0x00, 0xff, 0xff).

Kopumā, izmantojot pieejamos testa datus, iespējams iekļauties mazāk nekā 250Kb saspiestos datos.

Nedaudz vairāk var ietaupÄ«t, veicot bitu žonglÄ“Å”anu: pagaidām mēs ignorējam dažu nulles bitu klātbÅ«tni bloka beigās, daži biti bloka sākumā arÄ« nemainās...
Bet tad es pieņēmu stingru lēmumu apstāties, pretējā gadÄ«jumā Ŕādā tempā es varētu izstrādāt savu arhivētāju.

Kopumā no maniem testa datiem es saņēmu 3-4 baitus vienā ierakstā, saspieÅ”anas pakāpe izrādÄ«jās lielāka par 6:1. TeikÅ”u godÄ«gi: nebiju gaidÄ«jis tādu rezultātu, manuprāt, viss labāks par 2:1 jau ir rezultāts, kas attaisno kompresijas lietoÅ”anu.

Viss ir kārtÄ«bā, bet zlib (deflate) joprojām ir arhaisks, pelnÄ«ts un nedaudz vecmodÄ«gs kompresijas algoritms. Tas vien, ka pēdējie 32 Kb nesaspiestās datu straumes tiek izmantoti kā vārdnÄ«ca, Å”odien izskatās dÄ«vaini (tas ir, ja kāds datu bloks ir ļoti lÄ«dzÄ«gs tam, kas bija ievades straumē pirms 40 Kb, tad to atkal sāks arhivēt, un neattieksies uz iepriekŔēju notikumu). Modernajos arhivētajos vārdnÄ«cas lielums bieži tiek mērÄ«ts megabaitos, nevis kilobaitos.

Tāpēc mēs turpinām savu arhivētāju mini pētījumu.

Tālāk mēs pārbaudījām bzip2 (atcerieties, bez FLUSH tas uzrādīja fantastisku kompresijas pakāpi gandrīz 100:1). Diemžēl ar FLUSH tas darbojās ļoti slikti; saspiesto datu izmērs izrādījās lielāks nekā nesaspiesto.

Mani pieņēmumi par neveiksmes iemesliem

Libbz2 piedāvā tikai vienu skaloÅ”anas opciju, kas, Ŕķiet, notÄ«ra vārdnÄ«cu (analogs Z_FULL_FLUSH zlib); pēc tam nav runas par efektÄ«vu saspieÅ”anu.

Un pēdējais, kas tika pārbaudīts, bija zstd. Atkarībā no parametriem tas saspiež vai nu gzip līmenī, bet daudz ātrāk vai labāk nekā gzip.

Diemžēl ar FLUSH tas nedarbojās Ä«paÅ”i labi: saspiesto datu apjoms bija aptuveni 700 Kb.

ŠÆ uzdeva jautājumu projekta github lapā saņēmu atbildi, ka katram saspiesto datu blokam jārēķinās ar lÄ«dz 10 baitiem servisa datu, kas ir tuvu iegÅ«tajiem rezultātiem; deflāciju nevar panākt.

Es nolēmu pie Ŕī punkta apstāties savos eksperimentos ar arhivētājiem (atgādināŔu, ka xz, lzip, lzo, lz4 pat testÄ“Å”anas stadijā bez FLUSH neparādÄ«jās, un es neuzskatÄ«ju par eksotiskākiem kompresijas algoritmiem).

AtgriezÄ«simies pie arhivÄ“Å”anas problēmām.

Otra (kā saka secÄ«bā, nevis vērtÄ«bā) problēma ir tā, ka saspiestie dati ir viena straume, kurā pastāvÄ«gi ir atsauces uz iepriekŔējām sadaļām. Tādējādi, ja tiek bojāta saspiesto datu sadaļa, mēs zaudējam ne tikai saistÄ«to nesaspiesto datu bloku, bet arÄ« visus nākamos.

Å Ä«s problēmas risināŔanai ir Ŕāda pieeja:

  1. Novērsiet problēmas raÅ”anos ā€“ pievienojiet saspiestajiem datiem dublÄ“Å”anos, kas ļaus identificēt un labot kļūdas; mēs par to runāsim vēlāk;
  2. Samaziniet sekas, ja rodas problēma
    Mēs jau teicām iepriekÅ”, ka jÅ«s varat saspiest katru datu bloku neatkarÄ«gi, un problēma pazudÄ«s pati (viena bloka datu bojājums novedÄ«s pie datu zuduma tikai Å”im blokam). Tomēr Å”is ir ārkārtējs gadÄ«jums, kad datu saspieÅ”ana bÅ«s neefektÄ«va. Pretēja galējÄ«ba: izmantojiet visus 4 MB mÅ«su mikroshēmas kā vienotu arhÄ«vu, kas dos mums lielisku saspieÅ”anu, bet katastrofālas sekas datu bojājumu gadÄ«jumā.
    Jā, ir nepiecieÅ”ams kompromiss attiecÄ«bā uz uzticamÄ«bu. Taču jāatceras, ka mēs izstrādājam datu uzglabāŔanas formātu nemainÄ«gai atmiņai ar ārkārtÄ«gi zemu BER un deklarēto datu glabāŔanas periodu 20 gadi.

Eksperimentu laikā es atklāju, ka vairāk vai mazāk pamanāmi kompresijas līmeņa zudumi sākas saspiestu datu blokos, kuru izmērs ir mazāks par 10 KB.
IepriekÅ” tika minēts, ka izmantotā atmiņa ir lappuse, es neredzu iemeslu, kāpēc nevajadzētu izmantot korespondenci ā€œviena lapa - viens saspiestu datu bloksā€.

Tas ir, minimālais pieņemamais lapas izmērs ir 16 Kb (ar rezervi pakalpojuma informācijai). Tomēr tik mazs lapas izmērs uzliek ievērojamus ierobežojumus maksimālajam ieraksta izmēram.

Lai gan es vēl negaidu, ka saspiestā veidā ieraksti bÅ«s lielāki par dažiem kilobaitiem, es nolēmu izmantot 32 Kb lapas (kopā 128 lappuses vienā mikroshēmā).

Kopsavilkums:

  • Mēs uzglabājam datus, kas saspiesti, izmantojot zlib (deflate);
  • Katram ierakstam mēs iestatām Z_SYNC_FLUSH;
  • Katram saspiestajam ierakstam mēs apgriežam beigu baitus (piemēram, 0x00, 0x00, 0xff, 0xff); galvenē norādām, cik baitu nogriezām;
  • Mēs glabājam datus 32Kb lapās; lapas iekÅ”pusē ir viena saspiestu datu plÅ«sma; Katrā lapā mēs atkal sākam saspieÅ”anu.

Un pirms saspieÅ”anas pabeigÅ”anas vēlos vērst jÅ«su uzmanÄ«bu uz to, ka mums ir tikai daži baiti saspiestu datu uz vienu ierakstu, tāpēc ir ārkārtÄ«gi svarÄ«gi nepalielināt pakalpojuma informāciju, jo katrs baits Å”eit ir svarÄ«gs.

Datu galvenes glabāŔana

Tā kā mums ir mainīga garuma ieraksti, mums kaut kā jānosaka ierakstu izvietojums/robežas.

Es zinu trīs pieejas:

  1. Visi ieraksti tiek saglabāti nepārtrauktā straumē, vispirms ir ieraksta galvene ar garumu un pēc tam pats ieraksts.
    Šajā iemiesojumā gan galvenes, gan dati var būt dažāda garuma.
    BÅ«tÄ«bā mēs iegÅ«stam atseviŔķi saistÄ«tu sarakstu, kas tiek izmantots visu laiku;
  2. Galvenes un paŔi ieraksti tiek glabāti atseviŔķās plūsmās.
    Izmantojot nemainÄ«ga garuma galvenes, mēs nodroÅ”inām, ka vienas galvenes bojājumi neietekmē pārējās.
    Līdzīga pieeja tiek izmantota, piemēram, daudzās failu sistēmās;
  3. Ieraksti tiek glabāti nepārtrauktā plÅ«smā, ieraksta robežu nosaka noteikts marÄ·ieris (rakstzÄ«me/rakstzÄ«mju secÄ«ba, kas ir aizliegta datu blokos). Ja ieraksta iekÅ”pusē ir marÄ·ieris, mēs to aizstājam ar kādu secÄ«bu (aizveram to).
    Līdzīga pieeja tiek izmantota, piemēram, PPP protokolā.

Es ilustrēŔu.

Variants 1:
Mana gredzena bufera ievieÅ”ana NOR zibspuldzē
Å eit viss ir ļoti vienkārÅ”i: zinot ieraksta garumu, mēs varam aprēķināt nākamās galvenes adresi. Tātad mēs virzāmies pa virsrakstiem, lÄ«dz sastopam apgabalu, kas piepildÄ«ts ar 0xff (brÄ«vā zona) vai lapas beigām.

Variants 2:
Mana gredzena bufera ievieÅ”ana NOR zibspuldzē
MainÄ«ga ieraksta garuma dēļ mēs nevaram iepriekÅ” pateikt, cik ierakstu (un lÄ«dz ar to arÄ« galvenes) mums bÅ«s nepiecieÅ”ams vienā lapā. JÅ«s varat izkliedēt galvenes un paÅ”us datus dažādās lapās, bet es dodu priekÅ”roku citai pieejai: mēs ievietojam gan galvenes, gan datus vienā lapā, bet galvenes (konstanta izmēra) nāk no lapas sākuma, un dati (mainÄ«ga garuma) nāk no beigām. TiklÄ«dz viņi ā€œsatiekasā€ (nav pietiekami daudz brÄ«vas vietas jaunam ierakstam), mēs uzskatām, ka Ŕī lapa ir pabeigta.

Variants 3:
Mana gredzena bufera ievieÅ”ana NOR zibspuldzē
Galvenē nav nepiecieÅ”ams saglabāt datu garumu vai citu informāciju par datu atraÅ”anās vietu, pietiek ar marÄ·ieriem, kas norāda ierakstu robežas. Taču dati ir jāapstrādā rakstot/lasot.
Es izmantotu 0xff kā marÄ·ieri (kas pēc dzÄ“Å”anas aizpilda lapu), tāpēc brÄ«vā zona noteikti netiks uzskatÄ«ta par datiem.

Salīdzinājuma tabula:

iespēja 1
iespēja 2
iespēja 3

Kļūdu tolerance
Sākot no
+
+

Kompaktums
+
Sākot no
+

ÄŖstenoÅ”anas sarežģītÄ«ba
*
**
**

1. variantam ir liktenÄ«gs trÅ«kums: ja kāda no galvenēm ir bojāta, tiek iznÄ«cināta visa nākamā ķēde. AtlikuŔās opcijas ļauj atgÅ«t dažus datus pat lielu bojājumu gadÄ«jumā.
Bet Å”eit der atcerēties, ka mēs nolēmām datus glabāt saspiestā formā, un tāpēc mēs zaudējam visus lapas datus pēc ā€œsalauztaā€ ieraksta, tāpēc, lai arÄ« tabulā ir mÄ«nuss, mēs to nedarām. ņemt to vērā.

Kompaktums:

  • pirmajā variantā galvenē jāsaglabā tikai garums, ja izmantojam mainÄ«ga garuma veselus skaitļus, tad vairumā gadÄ«jumu varam iztikt ar vienu baitu;
  • otrajā variantā mums jāsaglabā sākuma adrese un garums; ierakstam jābÅ«t nemainÄ«gam izmēram, es lÄ“Å”u, ka katram ierakstam ir 4 baiti (divi baiti nobÄ«dei un divi baiti garumam);
  • treÅ”ajai opcijai nepiecieÅ”ama tikai viena rakstzÄ«me, lai norādÄ«tu ieraksta sākumu, kā arÄ« pats ieraksts palielināsies par 1-2% ekranÄ“Å”anas dēļ. Kopumā aptuveni paritāte ar pirmo variantu.

Sākotnēji es uzskatÄ«ju otro variantu par galveno (un pat uzrakstÄ«ju ievieÅ”anu). Es to pametu tikai tad, kad beidzot nolēmu izmantot kompresiju.

VarbÅ«t kādreiz es joprojām izmantoÅ”u lÄ«dzÄ«gu iespēju. Piemēram, ja man ir jānodarbojas ar datu glabāŔanu kuÄ£im, kas ceļo starp Zemi un Marsu, bÅ«s pavisam citas prasÄ«bas attiecÄ«bā uz uzticamÄ«bu, kosmisko starojumu, ...

Kas attiecas uz treÅ”o variantu: es tam pieŔķīru divas zvaigznes par ievieÅ”anas grÅ«tÄ«bām vienkārÅ”i tāpēc, ka man nepatÄ«k jaukties ar vairogu, mainÄ«t garumu procesā utt. Jā, iespējams, esmu neobjektÄ«vs, bet man bÅ«s jāieraksta kods ā€” kāpēc piespiest sevi darÄ«t kaut ko, kas jums nepatÄ«k.

Kopsavilkums: Mēs izvēlamies uzglabāŔanas iespēju ķēžu veidā ā€œgalvene ar garumu - mainÄ«ga garuma datiā€ efektivitātes un ievieÅ”anas vienkārŔības dēļ.

Bitu lauku izmantoŔana, lai uzraudzītu rakstīŔanas darbību panākumus

Es tagad neatceros, kur man radās ideja, bet tas izskatās apmēram Ŕādi:
Katram ierakstam mēs pieŔķiram vairākus bitus, lai saglabātu karogus.
Kā jau teicām iepriekÅ”, pēc dzÄ“Å”anas visi biti tiek aizpildÄ«ti ar 1, un mēs varam mainÄ«t 1 uz 0, bet ne otrādi. Tātad ā€œkarogs nav iestatÄ«tsā€ mēs izmantojam 1, bet ā€œkarogs ir iestatÄ«tsā€ mēs izmantojam 0.

LÅ«k, kā varētu izskatÄ«ties mainÄ«ga garuma ieraksta ievietoÅ”ana zibatmiņā:

  1. Iestatiet karogu ā€œgaruma ieraksts ir sāktsā€;
  2. Pierakstiet garumu;
  3. Iestatiet karogu ā€œdatu ierakstÄ«Å”ana ir sākusiesā€;
  4. Mēs ierakstām datus;
  5. Iestatiet karodziņu ā€œierakstÄ«Å”ana beigusiesā€.

Turklāt mums bÅ«s karodziņŔ ā€œnotikusi kļūdaā€, kopumā 4 bitu karodziņiem.

Å ajā gadÄ«jumā mums ir divi stabili stāvokļi "1111" - ierakstÄ«Å”ana nav sākta un "1000" - ierakstÄ«Å”ana bija veiksmÄ«ga; neparedzēta ierakstÄ«Å”anas procesa pārtraukuma gadÄ«jumā mēs saņemsim starpstāvokli, kurus pēc tam varēsim atklāt un apstrādāt.

Pieeja ir interesanta, taču tā pasargā tikai no pēkŔņiem elektrÄ«bas padeves pārtraukumiem un lÄ«dzÄ«gām kļūmēm, kas, protams, ir svarÄ«gi, taču tas nebÅ«t nav vienÄ«gais (vai pat galvenais) iespējamo kļūmju cēlonis.

Kopsavilkums: Dosimies tālāk, meklējot labu risinājumu.

Kontrolsummas

Kontrolsummas arÄ« ļauj pārliecināties (ar saprātÄ«gu varbÅ«tÄ«bu), ka mēs lasām tieÅ”i to, kas bija rakstÄ«ts. Un atŔķirÄ«bā no iepriekÅ” apskatÄ«tajiem bitu laukiem tie vienmēr darbojas.

Ja ņemam vērā iespējamo problēmu avotu sarakstu, par kuru mēs runājām iepriekÅ”, tad kontrolsumma spēj atpazÄ«t kļūdu neatkarÄ«gi no tās izcelsmes. (izņemot, iespējams, ļaunprātÄ«gos citplanētieÅ”us - viņi var arÄ« viltot kontrolsummu).

Tātad, ja mūsu mērķis ir pārbaudīt, vai dati ir neskarti, kontrolsummas ir lieliska ideja.

Kontrolsummas aprēķināŔanas algoritma izvēle neradÄ«ja jautājumus - CRC. No vienas puses, matemātiskās Ä«paŔības ļauj 100% uztvert noteikta veida kļūdas, no otras puses, uz nejauÅ”iem datiem Å”is algoritms parasti parāda sadursmju iespējamÄ«bu, kas nav daudz lielāka par teorētisko robežu. Mana gredzena bufera ievieÅ”ana NOR zibspuldzē. Tas var nebÅ«t ātrākais algoritms, ne vienmēr tas ir minimālais sadursmju skaita ziņā, taču tam ir ļoti svarÄ«ga kvalitāte: testos, ar kuriem es saskāros, nebija neviena modeļa, kurā tas nepārprotami bÅ«tu neveiksmÄ«gs. Stabilitāte Å”ajā gadÄ«jumā ir galvenā kvalitāte.

Tilpuma pētījuma piemērs: 1. daļa, 2. daļa (saites uz narod.ru, atvainojiet).

Tomēr kontrolsummas atlases uzdevums nav pabeigts; CRC ir vesela kontrolsummu saime. Jums jāizlemj par garumu un pēc tam jāizvēlas polinoms.

Kontrolsummas garuma izvēle nav tik vienkārÅ”s jautājums, kā Ŕķiet no pirmā acu uzmetiena.

Ļaujiet man ilustrēt:
Noskaidrosim kļūdas iespējamÄ«bu katrā baitā Mana gredzena bufera ievieÅ”ana NOR zibspuldzē un ideāla kontrolsumma, aprēķināsim vidējo kļūdu skaitu uz miljonu ierakstu:

Dati, baits
Kontrolsumma, baits
Neatklātas kļūdas
Viltus kļūdu noteikŔana
Kopējais viltus pozitīvais rādītājs

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

Å Ä·iet, ka viss ir vienkārÅ”i - atkarÄ«bā no aizsargājamo datu garuma izvēlieties kontrolsummas garumu ar minimālu nepareizu pozitÄ«vu skaitu - un triks ir maisā.

Tomēr problēma rodas ar Ä«sām kontrolsummām: lai gan tās labi atklāj viena bita kļūdas, tās ar diezgan lielu varbÅ«tÄ«bu var pieņemt pilnÄ«gi nejauÅ”us datus kā pareizus. Par Habrē jau bija raksts, kurā bija aprakstÄ«ts problēma reālajā dzÄ«vē.

Tāpēc, lai nejauÅ”as kontrolsummas sakritÄ«ba bÅ«tu gandrÄ«z neiespējama, jums jāizmanto kontrolsummas, kuru garums ir 32 biti vai ilgāk. (ja garums ir lielāks par 64 bitiem, parasti tiek izmantotas kriptogrāfiskās jaucējfunkcijas).

Neskatoties uz to, ka iepriekÅ” rakstÄ«ju, ka mums ir nepiecieÅ”ams ietaupÄ«t vietu, mēs joprojām izmantosim 32 bitu kontrolsummu (16 biti ir par maz, sadursmes iespējamÄ«ba ir lielāka par 0.01% un 24 biti, jo tie saki, nav ne Å”eit, ne tur).

Å eit var rasties iebildums: vai, izvēloties kompresiju, mēs saglabājām katru baitu, lai tagad dotu 4 baitus uzreiz? Vai nebÅ«tu labāk nesaspiest vai pievienot kontrolsummu? Protams, nē, bez kompresijas nenozÄ«mē, ka mums nav nepiecieÅ”ama integritātes pārbaude.

Izvēloties polinomu, mēs neizgudrosim riteni no jauna, bet ņemsim tagad populāro CRC-32C.
Šis kods nosaka 6 bitu kļūdas paketēs līdz 22 baitiem (iespējams, mums tas ir visizplatītākais gadījums), 4 bitu kļūdas paketēs līdz 655 baitiem (pie mums arī bieži), 2 vai jebkuru nepāra skaitu bitu kļūdu paketēs. jebkura saprātīga garuma.

Ja kādu interesē detaļas

Wikipedia raksts par CRC.

Koda parametri crc-32c par Koopman vietne ā€” iespējams, vadoÅ”ais CRC speciālists uz planētas.

Š’ viņa raksts tur ir vēl viens interesants kods, kas nodroÅ”ina nedaudz labākus parametrus mums aktuālajiem pakeÅ”u garumiem, taču atŔķirÄ«bu neuzskatÄ«ju par bÅ«tisku un biju pietiekami kompetenta, lai izvēlētos pielāgotu kodu standarta un labi izpētÄ«tā koda vietā.

Turklāt, tā kā mūsu dati ir saspiesti, rodas jautājums: vai mums jāaprēķina saspiesto vai nesaspiesto datu kontrolsumma?

Argumenti par labu nesaspiestu datu kontrolsummas aprēķināŔanai:

  • Mums beigu beigās ir jāpārbauda datu glabāŔanas droŔība - tātad pārbaudām to tieÅ”i (vienlaikus tiks pārbaudÄ«tas iespējamās kļūdas kompresijas/dekompresijas realizācijā, bojātas atmiņas radÄ«tie bojājumi utt.);
  • Deflācijas algoritmam zlib ir diezgan nobriedusi ievieÅ”ana un nevajadzētu krÄ«t ar ā€œgreiziemā€ ievades datiem; turklāt bieži vien spēj patstāvÄ«gi atklāt kļūdas ievades straumē, samazinot kopējo kļūdas neatklāŔanas varbÅ«tÄ«bu (veikta pārbaude ar viena bita invertÄ“Å”anu Ä«sajā ierakstā, zlib atklāja kļūdu apmēram treÅ”daļā gadÄ«jumu).

Argumenti pret nesaspiestu datu kontrolsummas aprēķināŔanu:

  • CRC ir Ä«paÅ”i ā€œpielāgotsā€ dažām bitu kļūdām, kas raksturÄ«gas zibatmiņai (bitu kļūda saspiestā straumē var izraisÄ«t lielas izmaiņas izvades straumē, kurā tÄ«ri teorētiski mēs varam ā€œnoÄ·ertā€ sadursmi);
  • Man ļoti nepatÄ«k ideja nodot potenciāli bojātus datus dekompresoram, Kas to lai zinakā viņŔ reaģēs.

Å ajā projektā es nolēmu atkāpties no vispārpieņemtās nesaspiestu datu kontrolsummas glabāŔanas prakses.

Kopsavilkums: Mēs izmantojam CRC-32C, mēs aprēķinām kontrolsummu no datiem tādā formā, kādā tie ir rakstÄ«ti uz flash (pēc saspieÅ”anas).

AtlaiŔana

Liekas kodÄ“Å”anas izmantoÅ”ana, protams, nenovērÅ” datu zudumu, tomēr tā var ievērojami (bieži vien par daudzām kārtām) samazināt neatgriezeniska datu zuduma iespējamÄ«bu.

Mēs varam izmantot dažāda veida dublÄ“Å”anu, lai labotu kļūdas.
Hamminga kodi var labot viena bita kļūdas, Rīda-Zālamana rakstzīmju kodus, vairākas datu kopijas apvienojumā ar kontrolsummām vai kodējumus, piemēram, RAID-6, var palīdzēt atgūt datus pat lielas korupcijas gadījumā.
Sākotnēji es biju apņēmies plaÅ”i izmantot kļūdām izturÄ«gu kodÄ“Å”anu, bet tad es sapratu, ka mums vispirms ir nepiecieÅ”ams priekÅ”stats par to, no kādām kļūdām mēs vēlamies sevi pasargāt, un pēc tam izvēlēties kodÄ“Å”anu.

IepriekÅ” teicām, ka kļūdas ir jānovērÅ” pēc iespējas ātrāk. Kādos gadÄ«jumos mēs varam saskarties ar kļūdām?

  1. Nepabeigts ieraksts (kādu iemeslu dēļ ierakstÄ«Å”anas laikā strāva tika izslēgta, Raspberry sastinga, ...)
    Diemžēl Ŕādas kļūdas gadÄ«jumā atliek tikai ignorēt nederÄ«gos ierakstus un uzskatÄ«t, ka dati ir zaudēti;
  2. RakstÄ«Å”anas kļūdas (kādu iemeslu dēļ tas, kas tika ierakstÄ«ts zibatmiņā, nebija tas, kas tika ierakstÄ«ts)
    Mēs varam nekavējoties atklāt Ŕādas kļūdas, ja mēs veicam testa nolasÄ«Å”anu tÅ«lÄ«t pēc ierakstÄ«Å”anas;
  3. Datu sagrozÄ«Å”ana atmiņā uzglabāŔanas laikā;
  4. LasīŔanas kļūdas
    Lai to labotu, ja kontrolsumma nesakrīt, pietiek ar rādījumu atkārtot vairākas reizes.

Tas ir, tikai treŔā veida kļūdas (spontāna datu sabojāŔana uzglabāŔanas laikā) nevar labot bez kļūdām izturÄ«gas kodÄ“Å”anas. Å Ä·iet, ka Ŕādas kļūdas joprojām ir ārkārtÄ«gi maz ticamas.

Kopsavilkums: tika nolemts atteikties no liekās kodÄ“Å”anas, bet, ja darbÄ«ba uzrāda Ŕī lēmuma kļūdu, tad atgriezties pie jautājuma izskatÄ«Å”anas (ar jau uzkrāto statistiku par kļūmēm, kas ļaus izvēlēties optimālo kodÄ“Å”anas veidu).

Cits

Protams, raksta formāts neļauj mums attaisnot katru formāta bitu (un spēki jau ir beiguÅ”ies), tāpēc es Ä«sumā apskatÄ«Å”u dažus punktus, kas iepriekÅ” netika skarti.

  • Tika nolemts visas lapas padarÄ«t ā€œvienādasā€
    Tas ir, nebÅ«s Ä«paÅ”u lapu ar metadatiem, atseviŔķiem pavedieniem utt., bet gan viens pavediens, kas pārraksta visas lapas pēc kārtas.
    Tas nodroÅ”ina vienmērÄ«gu lapu nodilumu, neviena neveiksmes punkta, un man tas vienkārÅ”i patÄ«k;
  • Ir obligāti jānodroÅ”ina formāta versija.
    Formāts bez versijas numura galvenē ir ļauns!
    Pietiek lapas galvenē pievienot lauku ar noteiktu burvju numuru (parakstu), kas norādīs izmantotā formāta versiju (Nedomāju, ka praksē tādu būs pat ducis);
  • Ierakstiem (kuru ir daudz) izmantojiet mainÄ«ga garuma galveni, vairumā gadÄ«jumu mēģinot padarÄ«t to 1 baitu garu;
  • Lai kodētu saspiestā ieraksta galvenes garumu un apgrieztās daļas garumu, izmantojiet mainÄ«ga garuma bināros kodus.

Ä»oti palÄ«dzēja tieÅ”saistes Ä£enerators Hafmena kodi. Tikai dažu minÅ«Å”u laikā mēs varējām atlasÄ«t nepiecieÅ”amos mainÄ«ga garuma kodus.

Datu uzglabāŔanas formāta apraksts

Baitu secība

Lauki, kas ir lielāki par vienu baitu, tiek saglabāti lielā formātā (tīkla baitu secībā), tas ir, 0x1234 tiek rakstīts kā 0x12, 0x34.

Lapu ŔķiroŔana

Visa zibatmiņa ir sadalīta vienāda izmēra lapās.

Noklusētais lapas izmērs ir 32Kb, bet ne vairāk kā 1/4 no kopējā atmiņas mikroshēmas izmēra (4MB mikroshēmai tiek iegūtas 128 lapas).

Katra lapa glabā datus neatkarīgi no pārējām (tas ir, dati vienā lapā neatsaucas uz datiem citā lapā).

Visas lapas ir numurētas dabiskā secÄ«bā (adreÅ”u augoŔā secÄ«bā), sākot ar numuru 0 (nulles lapa sākas ar adresi 0, pirmā lapa sākas ar 32Kb, otrā lapa sākas ar 64Kb utt.)

Atmiņas mikroshēma tiek izmantota kā ciklisks buferis (zvana buferis), tas ir, vispirms tiek rakstÄ«ts uz 0 lappusi, pēc tam numuru 1, ..., kad mēs aizpildām pēdējo lapu, sākas jauns cikls un ierakstÄ«Å”ana turpinās no nulles lapas. .

Lapas iekÅ”pusē

Mana gredzena bufera ievieÅ”ana NOR zibspuldzē
Lapas sākumā tiek saglabāta 4 baitu lapas galvene, pēc tam galvenes kontrolsumma (CRC-32C), pēc tam ieraksti tiek saglabāti formātā ā€œgalvenes, dati, kontrolsummaā€.

Lapas virsraksts (shēmā netÄ«ri zaļŔ) sastāv no:

  • divu baitu burvju numura lauks (arÄ« formāta versijas zÄ«me)
    paÅ”reizējai formāta versijai tas tiek aprēķināts kā 0xed00 āŠ• Š½Š¾Š¼ŠµŃ€ стрŠ°Š½Šøцы;
  • divu baitu skaitÄ«tājs ā€œLapas versijaā€ (atmiņas pārrakstÄ«Å”anas cikla numurs).

Lapas ieraksti tiek saglabāti saspiestā formā (tiek izmantots deflācijas algoritms). Visi ieraksti vienā lapā tiek saspiesti vienā pavedienā (tiek izmantota parastā vārdnÄ«ca), un katrā jaunā lapā saspieÅ”ana sākas no jauna. Tas ir, lai atspiestu jebkuru ierakstu, ir nepiecieÅ”ami visi iepriekŔējie ieraksti no Ŕīs lapas (un tikai Å”is).

Katrs ieraksts tiks saspiests ar karogu Z_SYNC_FLUSH, un saspiestās straumes beigās būs 4 baiti 0x00, 0x00, 0xff, 0xff, pirms kuriem, iespējams, būs vēl viens vai divi nulles baiti.
Mēs atmetam Å”o secÄ«bu (4, 5 vai 6 baitus garu), rakstot zibatmiņā.

Ieraksta galvene ir 1, 2 vai 3 baiti, kurā tiek glabāta:

  • viens bits (T), kas norāda ieraksta veidu: 0 - konteksts, 1 - žurnāls;
  • mainÄ«ga garuma lauks (S) no 1 lÄ«dz 7 bitiem, kas nosaka galvenes un ā€œastesā€ garumu, kas jāpievieno ierakstam dekompresijas nolÅ«kā;
  • rekorda garums (L).

S vērtību tabula:

S
Galvenes garums, baiti
Atmests rakstīŔanas laikā, baits

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)

Es mēģināju ilustrēt, es nezinu, cik skaidri tas izrādījās:
Mana gredzena bufera ievieÅ”ana NOR zibspuldzē
Dzeltens Å”eit apzÄ«mē T lauku, balts S lauku, zaļŔ L (saspiesto datu garums baitos), zils saspiestos datus, sarkans saspiesto datu pēdējos baitus, kas netiek ierakstÄ«ti zibatmiņā.

Tādējādi vienā baitā varam ierakstīt visizplatītāka garuma ierakstu galvenes (līdz 63+5 baitiem saspiestā veidā).

Pēc katra ieraksta tiek saglabāta CRC-32C kontrolsumma, kurā kā sākotnējā vērtÄ«ba (init) tiek izmantota iepriekŔējās kontrolsummas apgrieztā vērtÄ«ba.

CRC ir Ä«paŔība ā€œilgumsā€, darbojas Ŕāda formula (plus vai mÄ«nus bitu inversija procesā): Mana gredzena bufera ievieÅ”ana NOR zibspuldzē.
Tas nozÄ«mē, ka mēs aprēķinām CRC visiem iepriekŔējiem Ŕīs lapas galveņu un datu baitiem.

TieŔi aiz kontrolsummas ir nākamā ieraksta galvene.

Galvene ir veidota tā, lai tās pirmais baits vienmēr atŔķirtos no 0x00 un 0xff (ja galvenes pirmā baita vietā mēs sastopamies ar 0xff, tas nozÄ«mē, ka tas ir neizmantots apgabals; 0x00 signalizē par kļūdu).

Algoritmu piemēri

LasÄ«Å”ana no zibatmiņas

JebkurŔ lasījums nāk ar kontrolsummas pārbaudi.
Ja kontrolsumma nesakrīt, nolasījumu atkārto vairākas reizes, cerot nolasīt pareizos datus.

(tas ir loÄ£iski, Linux neglabā keÅ”atmiņā nolasÄ«jumus no NOR Flash, pārbaudÄ«ts)

Ierakstiet zibatmiņā

Mēs ierakstām datus.
Lasīsim tos.

Ja nolasītie dati nesakrīt ar rakstītajiem, laukumu aizpildām ar nullēm un signalizē par kļūdu.

Jaunas mikroshēmas sagatavoÅ”ana darbam

Inicializācijai galvene ar versiju 1 tiek ierakstīta pirmajā (vai drīzāk nulles) lapā.
Pēc tam Å”ajā lapā tiek ierakstÄ«ts sākotnējais konteksts (satur iekārtas UUID un noklusējuma iestatÄ«jumus).

Tas arÄ« viss, zibatmiņa ir gatava lietoÅ”anai.

MaŔīnas ielāde

Ielādējot, tiek nolasīti katras lapas pirmie 8 baiti (galvene + CRC), lapas ar nezināmu maģisko numuru vai nepareizu CRC tiek ignorētas.
No ā€œpareizajāmā€ lapām tiek atlasÄ«tas lapas ar maksimālo versiju, un no tām tiek ņemta lapa ar lielāko skaitu.
Tiek nolasÄ«ts pirmais ieraksts, tiek pārbaudÄ«ta CRC pareizÄ«ba un ā€œkontekstaā€ karoga klātbÅ«tne. Ja viss ir kārtÄ«bā, Ŕī lapa tiek uzskatÄ«ta par aktuālu. Ja nē, mēs atgriežamies pie iepriekŔējās lapas, lÄ«dz atrodam ā€œdzÄ«vuā€ lapu.
un atrastajā lapā lasām visus ierakstus, tos, kurus lietojam ar ā€œkontekstaā€ karogu.
Saglabājiet zlib vārdnīcu (tā būs nepiecieŔama, lai pievienotu Ŕo lapu).

Tas ir viss, lejupielāde ir pabeigta, konteksts ir atjaunots, jūs varat strādāt.

Žurnāla ieraksta pievienoŔana

Mēs saspiežam ierakstu ar pareizo vārdnÄ«cu, norādot Z_SYNC_FLUSH. Mēs redzam, vai saspiestais ieraksts iederas paÅ”reizējā lapā.
Ja tā neatbilst (vai lapā bija CRC kļūdas), sāciet jaunu lapu (skatiet tālāk).
Mēs pierakstām ierakstu un CRC. Ja rodas kļūda, sāciet jaunu lapu.

Jauna lapa

Mēs izvēlamies bezmaksas lapu ar minimālo skaitu (par bezmaksas lapu uzskatām lapu, kuras galvenē ir nepareiza kontrolsumma vai kuras versija ir mazāka par paÅ”reizējo). Ja Ŕādu lapu nav, atlasiet lapu ar minimālo skaitu no tām, kuru versija ir vienāda ar paÅ”reizējo.
Mēs izdzÄ“Å”am atlasÄ«to lapu. Mēs pārbaudām saturu ar 0xff. Ja kaut kas nav kārtÄ«bā, paņemiet nākamo bezmaksas lapu utt.
Izdzēstajā lapā ierakstām galveni, pirmais ieraksts ir paÅ”reizējais konteksta stāvoklis, nākamais ir nerakstÄ«tais žurnāla ieraksts (ja tāds ir).

Formāta pielietojamība

Manuprāt, tas izrādÄ«jās labs formāts jebkādu vairāk vai mazāk saspiežamu informācijas plÅ«smu (plain text, JSON, MessagePack, CBOR, iespējams, protobuf) glabāŔanai NOR Flash.

Protams, formāts ir ā€œpielāgotsā€ SLC NOR Flash.

To nevajadzētu izmantot ar augstu BER datu nesēju, piemēram, NAND vai MLC NOR (vai Ŕāda atmiņa vispār ir pieejama pārdoÅ”anā? Esmu redzējis, ka tā tiek pieminēta tikai laboÅ”anas kodu darbos).

Turklāt to nevajadzētu izmantot ierÄ«cēm, kurām ir savs FTL: USB zibatmiņa, SD, MicroSD utt (Ŕādai atmiņai es izveidoju formātu ar lapas izmēru 512 baiti, parakstu katras lapas sākumā un unikālos ierakstu numurus - dažkārt bija iespējams atgÅ«t visus datus no ā€œsagrautāā€ zibatmiņas diska ar vienkārÅ”u secÄ«gu lasÄ«Å”anu).

AtkarÄ«bā no uzdevumiem formātu var izmantot bez izmaiņām zibatmiņas diskos no 128Kbit (16Kb) lÄ«dz 1Gbit (128MB). Ja vēlaties, varat to izmantot lielākām mikroshēmām, taču, iespējams, jums ir jāpielāgo lapas izmērs (Bet Å”eit jau rodas jautājums par ekonomisko iespējamÄ«bu; cena par liela apjoma NOR Flash nav iepriecinoÅ”a).

Ja kādam formāts Ŕķiet interesants un vēlas to izmantot atvērtā projektā, rakstiet, mēģināŔu atrast laiku, noslÄ«pÄ“Å”u kodu un ievietoÅ”u github.

Secinājums

Kā redzat, beigās formāts izrādījās vienkārŔs un pat garlaicīgi.

Ir grÅ«ti atspoguļot mana viedokļa attÄ«stÄ«bu rakstā, bet ticiet man: sākotnēji es gribēju izveidot kaut ko izsmalcinātu, neiznÄ«cināmu, kas spēj pārdzÄ«vot pat kodolsprādzienu tieŔā tuvumā. Tomēr saprāts (es ceru) tomēr uzvarēja un pakāpeniski prioritātes pārcēlās uz vienkārŔību un kompaktumu.

Vai varētu būt, ka es kļūdījos? Jā, protams. Piemēram, var izrādīties, ka mēs iegādājāmies zemas kvalitātes mikroshēmu partiju. Vai arī kāda cita iemesla dēļ iekārta neatbilst uzticamības prasībām.

Vai man ir plāns Å”im nolÅ«kam? Es domāju, ka pēc raksta izlasÄ«Å”anas jums nav Å”aubu, ka plāns ir. Un pat ne vienatnē.

Nedaudz nopietnāk sakot, formāts tika izstrādāts gan kā darba iespēja, gan kā ā€œizmēģinājuma balonsā€.

Å obrÄ«d viss uz galda darbojas labi, burtiski citu dienu risinājums tiks izvietots (aptuveni) simtiem ierīču, redzēsim, kas notiek ā€œkaujasā€ darbÄ«bā (par laimi, es ceru, ka formāts ļauj droÅ”i noteikt kļūmes; lai jÅ«s varētu apkopot pilnu statistiku). Pēc dažiem mēneÅ”iem varēs izdarÄ«t secinājumus (un, ja jums nepaveicas, pat agrāk).

Ja, pamatojoties uz lietoÅ”anas rezultātiem, tiks atklātas nopietnas problēmas un nepiecieÅ”ami uzlabojumi, tad noteikti par to uzrakstÄ«Å”u.

Literatūra

Es negribēju izveidot garu un garlaicīgu lietoto darbu sarakstu; galu galā visiem ir Google.

Å eit es nolēmu atstāt sarakstu ar atradumiem, kas man Ŕķita Ä«paÅ”i interesanti, taču pamazām tie migrēja tieÅ”i raksta tekstā, un sarakstā palika viens vienums:

  1. LietderÄ«ba infgen no autora zlib. Var skaidri parādÄ«t deflate/zlib/gzip arhÄ«vu saturu. Ja jums ir jārisina deflācijas (vai gzip) formāta iekŔējā struktÅ«ra, es to ļoti iesaku.

Avots: www.habr.com

Pievieno komentāru