A mo implementazione di un ring buffer in NOR flash

Pristoria

Ci sò vending machines di u nostru propiu disignu. Dentru u Raspberry Pi è qualchì cablaggio nantu à una scheda separata. Un accettatore di munita, un accettore di billetta, un terminal bancariu sò cunnessi... Tuttu hè cuntrullatu da un prugramma auto-scrittu. L'intera storia di u travagliu hè scritta in un logu nantu à una unità flash (MicroSD), chì hè dopu trasmessa via Internet (utilizandu un modem USB) à u servitore, induve hè guardatu in una basa di dati. L'infurmazione di vendita hè caricata in 1c, ci hè ancu una interfaccia web simplice per u monitoraghju, etc.

Questu hè, u ghjurnale hè vitale - per a cuntabilità (ingudu, vendite, etc.), u monitoraghju (tutti i tipi di fallimenti è altre circustanze di forza maiò); Questu, unu puderia dì, hè tutte l'infurmazioni chì avemu nantu à sta macchina.

prublemu

L'unità flash si mostranu esse apparecchi assai inaffidabili. Fiascanu cun regularità envidiable. Stu porta à tramindui downtime machine è (s'è per qualchi mutivu u logu ùn pudia esse trasferitu nanu) à perdita di dati.

Questa ùn hè micca a prima sperienza di l'usu di unità flash, prima di questu ci era un altru prughjettu cù più di un centu di dispusitivi, induve a rivista hè stata guardata in unità flash USB, ci era ancu prublemi cù affidabilità, à volte u numeru di quelli chì fiascatu in un mese era in decine. Pruvamu diverse unità flash, cumpresi quelli di marca cù memoria SLC, è certi mudelli sò più affidabili di l'altri, ma rimpiazzà i flash drives ùn hà micca risolve radicali u prublema.

Attenzione! Longa lettura! Se ùn site micca interessatu in "perchè", ma solu in "cumu", pudete andà drittu À a fine articuli.

dicisioni

A prima cosa chì vene in mente hè: abbandunà MicroSD, installate, per esempiu, un SSD, è boot da questu. Teoricamente pussibule, prubabilmente, ma relativamente caru, è micca cusì affidabile (un adattatore USB-SATA hè aghjuntu; statistiche di fallimentu per i SSD di u budgetu ùn sò ancu incuragisce).

USB HDD ùn pare micca ancu una suluzione particularmente attrattiva.

Dunque, simu ghjunti à questa opzione: lascià l'iniziu da MicroSD, ma l'utilizanu in modu di sola lettura, è almacenà u logu di l'operazione (è altre informazioni uniche à un pezzu particulari di hardware - numeru di serie, calibrazioni di sensori, etc.) in un altru locu. .

U tema di FS di sola lettura per i lamponi hè digià statu studiatu in l'internu è fora, ùn aghju micca aspittà nantu à i dettagli di implementazione in questu articulu. (ma s'ellu ci hè interessu, forse scriveraghju un mini-articulu nantu à questu tema). L'unicu puntu chì vogliu nutà hè chì sia da l'esperienza persunale sia da e recensioni di quelli chì l'anu digià implementatu, ci hè un guadagnu in affidabilità. Iè, hè impussibile di sguassate cumplettamente di i guasti, ma riduce significativamente a so frequenza hè abbastanza pussibule. È e carte sò diventate unificate, chì facenu a sostituzione assai più faciule per u persunale di serviziu.

Hardware

Ùn ci era micca dubbitu particulari nantu à a scelta di u tipu di memoria - NOR Flash.
Argumenti:

  • cunnessione simplice (a maiò spessu l'autobus SPI, chì avete digià sperienza in usu, cusì ùn sò previsti prublemi di hardware);
  • prezzu ridiculu;
  • protocolu operativu standard (l'implementazione hè digià in u kernel Linux, se vulete, pudete piglià un terzu, chì hè ancu presente, o ancu scrive u vostru propiu, furtunamente tuttu hè simplice);
  • affidabilità è risorse:
    da una datasheet tipica: i dati sò almacenati per 20 anni, 100000 cicli di sguassà per ogni bloccu;
    da fonti di terzu: BER estremamente bassu, postulate micca bisognu di codici di correzione di errore (Alcune opere consideranu ECC per NOR, ma di solitu significanu sempre MLC NOR; questu succede ancu).

Stimemu i bisogni per u voluminu è e risorse.

Vogliu chì i dati sò guarantiti per esse salvati per parechji ghjorni. Questu hè necessariu per chì in casu di prublemi di cumunicazione, a storia di vendita ùn hè micca persa. Fighjemu nantu à 5 ghjorni, durante stu periodu (ancu tenendu in contu i weekend è i vacanze) u prublema pò esse risolta.

Attualmente cullemu circa 100 kb di logs per ghjornu (3-4 mila entrate), ma gradualmente sta figura cresce - u dettagliu hè in crescita, novi avvenimenti sò aghjuntu. In più, qualchì volta ci sò scoppi (alcuni sensori cumincianu à spammà cù falsi pusitivi, per esempiu). Calculeremu per 10 mila dischi 100 bytes ognunu - megabytes per ghjornu.

In totale, 5MB di dati puliti (ben compressi) esce. Più à elli (stima approssimativa) 1 MB di dati di serviziu.

Vale à dì, avemu bisognu di un chip 8MB si ùn avemu micca usu di cumpressione, o 4MB si avemu usatu. Numeri abbastanza realistichi per stu tipu di memoria.

In quantu à a risorsa: se pensamu chì tutta a memoria serà riscritta micca più di una volta ogni 5 ghjorni, allora più di 10 anni di serviziu avemu menu di mille cicli di riscrittura.
Lasciami ricurdà chì u fabricatore prumetti centu mila.

Un pocu nantu à NOR vs NAND

Oghje, sicuru, a memoria NAND hè assai più populari, ma ùn l'aghju micca aduprà per stu prughjettu: NAND, à u cuntrariu di NOR, hà bisognu di l'usu di codici di correzione d'errore, una tavola di blocchi cattivi, etc., è ancu e gambe di Chips NAND di solitu assai di più.

I svantaghji di NOR includenu:

  • picculu vulume (è, dunque, prezzu altu per megabyte);
  • bassa velocità di cumunicazione (in gran parte per u fattu chì una interfaccia seriale hè aduprata, di solitu SPI o I2C);
  • cancellazione lenta (secondu a dimensione di u bloccu, ci vole da una frazzioni di una seconda à parechji seconde).

Sembra chì ùn ci hè nunda di criticu per noi, cusì cuntinuemu.

Se i dettagli sò interessanti, u microcircuit hè statu sceltu at25df321a (Tuttavia, questu ùn hè micca impurtante, ci sò assai analoghi nantu à u mercatu chì sò cumpatibili in u sistema di pinout è di cumandamentu; ancu s'è vulemu installà un microcircuit da un fabricatore diversu è / o una dimensione diversa, tuttu funziona senza cambià u codice).

Aduprà u driver integratu in u kernel Linux; nantu à Raspberry, grazia à u supportu di l'arburu di u dispusitivu, tuttu hè assai simplice - avete bisognu di mette a superposizione compilata in /boot/overlays è mudificà ligeramente /boot/config.txt.

Esempiu di file dts

Per esse onesto, ùn sò micca sicuru chì hè scrittu senza errore, ma funziona.

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

È una altra linea in config.txt

dtoverlay=at25:spimaxfrequency=50000000

Ometteraghju a descrizzione di cunnette u chip à u Raspberry Pi. Da una banda, ùn sò micca un espertu in l'elettronica, da l'altra banda, tuttu quì hè banale ancu per mè: u microcircuit hà solu 8 gambe, di quale avemu bisognu di terra, putenza, SPI (CS, SI, SO, SCK). ); i livelli sò uguali à quelli di u Raspberry Pi, ùn hè micca necessariu un cablaggio supplementu - basta cunnette i pins 6 indicati.

Formulazione di u prublema

Comu solitu, a dichjarazione di u prublema passa per parechje iterazioni, è mi pare chì hè u tempu per u prossimu. Fermu dunque, mette inseme ciò chì hè digià scrittu, è chjarificà i ditagli chì fermanu in l'ombra.

Dunque, avemu decisu chì u logu serà guardatu in SPI NOR Flash.

Chì ghjè NOR Flash per quelli chì ùn sanu micca ?

Questa hè una memoria non volatile cù quale pudete fà trè operazioni:

  1. Lettura:
    A lettura più cumuna : trasmettemu l'indirizzu è leghje quanti byte avemu bisognu;
  2. Récordu:
    Scrive à NOR flash pare un regula, ma hà una peculiarità: pudete cambià solu da 1 à 0, ma micca vice versa. Per esempiu, se avemu avutu 0x55 in una cellula di memoria, dopu avè scrittu 0x0f, 0x05 serà digià almacenatu quì. (vede a tavula ghjustu sottu);
  3. Sguassà:
    Di sicuru, avemu bisognu di pudè fà l'operazione opposta - cambià da 0 à 1, questu hè esattamente ciò chì hè l'operazione di sguassà. A cuntrariu di i primi dui, ùn opera micca cù byte, ma cù blocchi (u bloccu minimu di sguassà in u chip sceltu hè 4kb). Erase distrugge u bloccu sanu è hè l'unicu modu per cambià da 0 à 1. Per quessa, quandu u travagliu cù a memoria flash, spessu deve allineà e strutture di dati à u cunfini di u bloccu di sguassà.
    Registrazione in NOR Flash:

Dati binari

Hè statu
01010101

Arregistratu
00001111

Hè diventatu
00000101

U logu stessu rapprisenta una sequenza di registri di lunghezza variabile. A durata tipica di un registru hè di circa 30 bytes (ancu se i registri chì sò parechji kilobyte di lunghezza si trovanu qualchì volta). In questu casu, avemu travagliatu cun elli solu cum'è un set di bytes, ma, se site interessatu, CBOR hè utilizatu in i registri.

In più di u logu, avemu bisognu di almacenà qualchi infurmazione "setting", sia aghjurnata è micca: un certu ID di u dispositivu, calibrazioni di sensori, una bandiera "disattivu temporaneamente disattivata", etc.
Questa informazione hè un inseme di registri chjave-valore, ancu almacenatu in CBOR. Ùn avemu micca assai di sta informazione (pochi kilobyte à u più), è hè aghjurnata pocu freti.
In ciò chì seguita, chjameremu u cuntestu.

Se ci ricurdate di induve stu articulu hà cuminciatu, hè assai impurtante per assicurà un almacenamentu affidabile di dati è, se pussibule, un funziunamentu cuntinuu ancu in casu di fallimenti hardware / corruzzione di dati.

Chì fonti di prublemi ponu esse cunsiderate?

  • Spegni durante l'operazione di scrittura / cancellazione. Questu hè di a categuria "ùn ci hè micca un truccu contr'à a crowbar".
    Infurmazione da discussioni nantu à stackexchange: quandu u putere hè spento mentre travaglia cù u flash, sia sguassate (impostu à 1) è scrive (impostu à 0) portanu à un cumpurtamentu indefinitu: i dati ponu esse scritti, parzialmente scritti (per dì, avemu trasferitu 10 bytes / 80 bits). , ma micca ancu solu 45 bits ponu esse scrittu), hè ancu pussibule chì alcuni di i bits seranu in un statu "intermediu" (a lettura pò pruduce sia 0 sia 1);
  • Errori in a memoria flash stessu.
    BER, benchì assai bassu, ùn pò esse uguali à zero;
  • Errori di autobus
    I dati trasmessi via SPI ùn sò micca prutetti in ogni modu; sia errori di un bit sia errori di sincronizazione ponu accade - perdita o inserimentu di bit (chì porta à una distorsione massiva di dati);
  • Altri errori / glitches
    Errori in u codice, glitches di Raspberry, interferenze straniere...

Aghju formulatu i requisiti, u so rispettu, in my opinion, hè necessariu per assicurà l'affidabilità:

  • i registri devenu andà immediatamente in memoria flash, e scritture ritardate ùn sò micca cunsiderate; - se si verifica un errore, deve esse rilevatu è processatu u più prestu pussibule; - u sistema deve, se pussibule, ricuperà da l'errori.
    (un esempiu da a vita "cumu ùn deve esse", chì pensu chì tutti anu scontru: dopu un reboot d'urgenza, u sistema di fugliale hè "rottu" è u sistema operatore ùn hè micca boot)

Idee, avvicinamenti, riflessioni

Quandu aghju cuminciatu à pensà à stu prublema, assai idee mi passavanu in capu, per esempiu:

  • aduprà cumpressione di dati;
  • Aduprate strutture di dati intelligenti, per esempiu, almacenà l'intestazione di u record separatamente da i registri stessi, perchè s'ellu ci hè un errore in ogni record, pudete leghje u restu senza prublemi;
  • aduprà campi di bit per cuntrullà u cumpletu di a registrazione quandu u putere hè disattivatu;
  • magazzini checksums per tuttu;
  • aduprate qualchì tipu di codificazione resistente à u rumore.

Alcune di sti idee sò stati utilizati, mentri àutri sò stati decisi à esse abbandunati. Andemu in ordine.

Cumpressione di dati

L'avvenimenti stessi chì avemu registratu in u ghjurnale sò assai simili è ripetibili ("tiru una munita di 5 rubli", "pressu u buttone per dà cambià", ...). Dunque, a cumpressione deve esse abbastanza efficace.

L'overhead di compressione hè insignificante (u nostru processore hè abbastanza putente, ancu u primu Pi avia un core cù una freccia di 700 MHz, i mudelli attuali anu parechji nuclei cù una freccia di più di un gigahertz), u scambiu cù u almacenamiento hè bassu (parechji). megabytes per seconda), a dimensione di i registri hè chjuca. In generale, se a compressione hà un impattu nantu à u rendiment, serà solu pusitivu. (assolutamente acriticu, solu dichjarà). In più, ùn avemu micca un veru embedded, ma un Linux regulare - cusì l'implementazione ùn deve micca bisognu di assai sforzu (hè abbastanza per ligà a biblioteca è aduprà parechje funzioni da ellu).

Un pezzu di u logu hè statu pigliatu da un dispositivu di travagliu (1.7 MB, 70 mila entrate) è prima verificatu per a compressione cù gzip, lz4, lzop, bzip2, xz, zstd dispunibule nantu à l'urdinatore.

  • gzip, xz, zstd hà mostratu risultati simili (40Kb).
    Eru surprised chì a moda xz si mostra quì à u livellu di gzip o zstd;
  • lzip cù i paràmetri predeterminati hà datu risultati un pocu peghju;
  • lz4 è lzop ùn anu micca risultati assai boni (150Kb);
  • bzip2 hà dimustratu un risultatu sorprendentemente bonu (18Kb).

Dunque, i dati sò cumpressi assai bè.
Allora (se ùn truvamu micca difetti fatali) ci sarà cumpressione! Semplicemente perchè più dati ponu mette nantu à u stessu flash drive.

Pensemu à i svantaghji.

Primu prublema: avemu digià accunsentutu chì ogni discu deve andà subitu à lampassi. Di genere, l'archiver raccoglie dati da u flussu di input finu à chì decide chì hè ora di scrive u weekend. Avemu bisognu di riceve immediatamente un bloccu di dati cumpressu è almacenà in memoria non volatile.

Vecu trè manere:

  1. Cumpressate ogni registru utilizendu a compressione di dizziunariu invece di l'algoritmi discututi sopra.
    Hè una opzione di travagliu cumplettamente, ma ùn mi piace micca. Per assicurà un livellu più o menu decentu di cumpressione, u dizziunariu deve esse "adattatu" à dati specifichi; ogni cambiamentu porta à u livellu di cumpressione caduta catastroficamente. Iè, u prublema pò esse risolta creendu una nova versione di u dizziunariu, ma questu hè un mal di testa - avemu bisognu di guardà tutte e versioni di u dizziunariu; in ogni entrata avemu bisognu di indicà cù quale versione di u dizziunariu hè stata cumpressa...
  2. Cumpressate ogni record usendu algoritmi "classici", ma indipindentamente di l'altri.
    L'algoritmi di cumpressione in cunsiderà ùn sò micca pensati per travaglià cù registri di questa dimensione (decine di bytes), u rapportu di compressione serà chjaramente menu di 1 (vale à dì, aumentendu u voluminu di dati invece di cumpressione);
  3. Fate FLUSH dopu ogni registrazione.
    Parechje biblioteche di compressione anu supportu per FLUSH. Questu hè un cumandamentu (o un paràmetru à a prucedura di cumpressione), dopu avè ricivutu chì l'archiver forma un flussu cumpressu in modu chì pò esse usatu per restaurà. tutte e dati uncompressed chì hè digià ricevutu. Un tali analogu sync in sistemi di schedari o commit in sql.
    Ciò chì hè impurtante hè chì l'operazioni di compressione successive puderanu aduprà u dizziunariu accumulatu è u rapportu di compressione ùn soffre micca quantu in a versione precedente.

Pensu chì hè ovvi chì aghju sceltu a terza opzione, fighjemu in più detail.

Truvatu grande articulu circa FLUSH in zlib.

Aghju fattu una prova di ghjinochju basatu annantu à l'articulu, hà pigliatu 70 mila entrate di log da un veru dispositivu, cù una dimensione di pagina di 60Kb (Turneremu à a dimensione di a pagina dopu) ricevutu:

Dati iniziali
Cumpressione gzip -9 (senza FLUSH)
zlib cù Z_PARTIAL_FLUSH
zlib cù Z_SYNC_FLUSH

Volume, KB
1692
40
352
604

À u primu sguardu, u prezzu cuntribuitu da FLUSH hè eccessivamente altu, ma in a realità avemu pocu scelta - o micca per cumpressione in tuttu, o per cumpressione (è assai efficace) cù FLUSH. Ùn ci vole à scurdà chì avemu 70 mila records, a redundancy introduttu da Z_PARTIAL_FLUSH hè solu 4-5 bytes per record. È u rapportu di compressione hè stata quasi 5: 1, chì hè più cà un risultatu excelente.

Pò esse una sorpresa, ma Z_SYNC_FLUSH hè in realtà un modu più efficace per fà FLUSH

Quandu si usa Z_SYNC_FLUSH, l'ultimi 4 bytes di ogni entrata seranu sempre 0x00, 0x00, 0xff, 0xff. È s'è no li cunnosci, tandu ùn avemu a almacenà elli, cusì a dimensione finale hè solu 324Kb.

L'articulu chì aghju ligatu hà una spiegazione:

Un novu bloccu di tipu 0 cù cuntenutu viotu hè aghjuntu.

Un bloccu di tipu 0 cù cuntenutu viotu hè custituitu da:

  • l'intestazione di bloccu di trè bit;
  • 0 à 7 bits uguali à zero, per ottene l'allineamentu di byte;
  • a sequenza di quattru byte 00 00 FF FF.

Comu pudete vede facilmente, in l'ultimu bloccu prima di questi 4 bytes ci sò da 3 à 10 zero bits. Tuttavia, a pratica hà dimustratu chì ci sò in realtà almenu 10 zero bits.

Risulta chì tali blocchi brevi di dati sò generalmente (sempre?) codificati utilizendu un bloccu di tipu 1 (bloccu fissu), chì necessariamente finisce cù 7 zero bits, dendu un totale di 10-17 zero bits garantiti (è u restu). esse zero cù una probabilità di circa 50%).

Dunque, nantu à i dati di teste, in u 100% di i casi ci hè un byte zero prima di 0x00, 0x00, 0xff, 0xff, è in più di un terzu di i casi ci sò dui byte zero. (forse u fattu hè chì aghju utilizatu CBOR binariu, è quandu si usa u testu JSON, blocchi di tipu 2 - bloccu dinamicu seranu più cumuni, rispettivamente, blocchi senza byte zero supplementari prima di 0x00, 0x00, 0xff, 0xff seranu scontri).

In u tutale, utilizendu i dati di prova dispunibuli, hè pussibule di mette in menu di 250Kb di dati cumpressi.

Pudete risparmià un pocu di più ghjugnendu bits: per avà ignurà a prisenza di uni pochi di bits zero à a fine di u bloccu, uni pochi di bit à l'iniziu di u bloccu ùn cambianu ancu ...
Ma dopu aghju pigliatu una decisione forte di vuluntà di piantà, altrimenti à questu ritmu puderia finisce per sviluppà u mo propiu archiviu.

In totale, da i mo dati di prova aghju ricevutu 3-4 bytes per scrittura, u rapportu di compressione hè diventatu più di 6: 1. Seraghju onestu: ùn m'aspittava micca un tali risultatu; in my opinion, qualcosa di megliu cà 2: 1 hè digià un risultatu chì ghjustificà l'usu di cumpressione.

Tuttu hè bè, ma zlib (deflate) hè sempre un algoritmu di cumpressione arcaicu, meritatu è pocu anticu. U mera fattu chì l'ultimi 32Kb di u flussu di dati senza cumpressione hè utilizatu cum'è un dizziunariu pare stranu oghje (vale à dì, se qualchì bloccu di dati hè assai simili à ciò chì era in u flussu di input 40Kb fà, allora hà da cumincià à esse archiviatu novu, è ùn si riferirà micca à una occorrenza precedente). In l'archiviari muderni di moda, a dimensione di u dizziunariu hè spessu misurata in megabyte invece di kilobytes.

Allora cuntinuemu u nostru mini-studiu di l'archivi.

Dopu avemu pruvatu bzip2 (ricurdatevi, senza FLUSH hà dimustratu una ratio di compressione fantastica di quasi 100: 1). Sfurtunatamente, hà fattu assai male cù FLUSH; a dimensione di i dati cumpressi hè diventatu più grande di i dati micca cumpressi.

I mo supposizioni nantu à i motivi di u fallimentu

Libbz2 offre una sola opzione di flush, chì pare chì sguassate u dizziunariu (analoga à Z_FULL_FLUSH in zlib); ùn si parla micca di cumpressione efficace dopu.

È l'ultimu per esse pruvatu era zstd. Sicondu i paràmetri, comprime sia à u livellu di gzip, ma assai più veloce, o megliu cà gzip.

Alas, cù FLUSH ùn hà micca fattu assai bè: a dimensione di i dati cumpressi era di circa 700Kb.

Я hà fattu una dumanda nantu à a pagina di github di u prughjettu, aghju ricevutu una risposta chì duvete cuntà finu à 10 bytes di dati di serviziu per ogni bloccu di dati cumpressi, chì hè vicinu à i risultati ottenuti; ùn ci hè micca manera di piglià cun deflate.

Aghju decisu di piantà à questu puntu in i mo esperimenti cù l'archivi (lasciami ricurdà chì xz, lzip, lzo, lz4 ùn si mostranu micca ancu in u stadiu di prova senza FLUSH, è ùn aghju micca cunsideratu algoritmi di compressione più esotici).

Riturnemu à i prublemi di archiviazione.

U sicondu (cum'è dicenu in ordine, micca in valore) prublema hè chì i dati cumpressi sò un flussu unicu, in quale ci sò constantemente riferimenti à e sezzioni previ. Cusì, s'è una rùbbrica di dati cumpressu hè guastatu, ùn perdemu micca solu u bloccu assuciatu di dati uncompressed, ma dinù tutti i seguenti.

Ci hè un approcciu per risolve stu prublema:

  1. Impedisce u prublema da esse - aghjunghje redundancy à i dati cumpressi, chì vi permetterà di identificà è corregge l'errori; ne parleremu più tardi;
  2. Minimizà e cunsequenze in casu di prublema
    Avemu digià dettu prima chì pudete cumpressà ogni bloccu di dati indipindentamente, è u prublema sparirà da ellu stessu (i danni à i dati di un bloccu portanu à a perdita di dati solu per questu bloccu). Tuttavia, questu hè un casu estremu in quale a compressione di dati serà inefficace. L'estremu cuntrariu: utilizate tutti i 4MB di u nostru chip cum'è un archiviu unicu, chì ci darà una compressione eccellente, ma cunsiquenzi catastròfichi in casu di corruzzione di dati.
    Iè, un cumprumissu hè necessariu in termini di affidabilità. Ma duvemu ricurdà chì sviluppemu un furmatu di almacenamiento di dati per a memoria non volatile cù un BER estremamente bassu è un periodu di almacenamiento di dati dichjaratu di 20 anni.

Duranti l'esperimenti, aghju scupertu chì i perditi più o menu notevuli in u livellu di cumpressione cumincianu nantu à blocchi di dati cumpressi menu di 10 KB in grandezza.
Hè statu citatu prima chì a memoria utilizata hè paginata; ùn vecu micca ragiuni perchè a corrispondenza "una pagina - un bloccu di dati cumpressi" ùn deve esse usata.

Questu hè, a dimensione minima di pagina raghjone hè 16Kb (cù una riserva per l'infurmazioni di serviziu). Tuttavia, una dimensione di pagina cusì chjuca impone restrizioni significative à a dimensione massima di u record.

Ancu s'ellu ùn aghju micca aspittatu ancu di record più grande di uni pochi kilobyte in forma compressa, aghju decisu d'utilizà pagine 32Kb (per un totale di pagine 128 per chip).

Riassuntu:

  • Almacenemu dati cumpressi cù zlib (deflate);
  • Per ogni entrata avemu stabilitu Z_SYNC_FLUSH;
  • Per ogni registru cumpressatu, trimemu i bytes di fine (per esempiu 0x00, 0x00, 0xff, 0xff); in l'intestazione indichemu quanti bytes avemu tagliatu;
  • Avemu guardatu dati in pagine 32Kb; ci hè un unicu flussu di dati cumpressi in a pagina; In ogni pagina avemu principiatu a compressione di novu.

E, prima di finisce cù a compressione, vogliu attirà a vostra attenzione à u fattu chì avemu solu uni pochi di byte di dati cumpressi per record, per quessa, hè assai impurtante per ùn inflate l'infurmazioni di serviziu, ogni byte conta quì.

Archiviazione di l'intestazione di dati

Siccomu avemu registri di lunghezza variabile, avemu bisognu di determinà in qualchì manera a piazza / cunfini di i registri.

Sò trè approcci:

  1. Tutti i registri sò guardati in un flussu cuntinuu, prima ci hè un intestazione di registrazione chì cuntene a durata, è dopu u discu stessu.
    In questa incarnazione, sia intestazioni è dati ponu esse di lunghezza variabile.
    Essenzialmente, avemu una lista ligata singola chì hè utilizata tuttu u tempu;
  2. L'intestazione è i registri stessi sò almacenati in flussi separati.
    Utilizendu intestazioni di lunghezza constante, assicuremu chì i danni à un capu ùn affettanu micca l'altri.
    Un approcciu simili hè utilizatu, per esempiu, in parechji sistemi di schedari;
  3. I registri sò almacenati in un flussu cuntinuu, u cunfini di u record hè determinatu da un certu marcatu (un caratteru / sequenza di caratteri chì hè pruibitu in blocchi di dati). S'ellu ci hè un marcatore in u discu, allora l'avemu rimpiazzatu cù una sequenza (scap it).
    Un approcciu simili hè utilizatu, per esempiu, in u protocolu PPP.

Illustreraghju.

Opzione 1:
A mo implementazione di un ring buffer in NOR flash
Tuttu hè assai simplice quì: sapendu a durata di u record, pudemu calculà l'indirizzu di l'intestazione dopu. Allora andemu per l'intestazione finu à chì scontru una zona piena di 0xff (zona libera) o a fine di a pagina.

Opzione 2:
A mo implementazione di un ring buffer in NOR flash
A causa di a durata variabile di u record, ùn pudemu micca dì in anticipu quanti registri (è dunque intestazioni) avemu bisognu per pagina. Pudete sparghje l'intestazione è e dati stessi in diverse pagine, ma preferimu un accostu diversu: pusemu l'intestazione è e dati nantu à una pagina, ma l'intestazione (di dimensione constante) venenu da u principiu di a pagina, è u data (di lunghezza variabile) vene da a fine. Appena si "scontranu" (ùn ci hè micca abbastanza spaziu liberu per una nova entrata), cunsideremu sta pagina cumpleta.

Opzione 3:
A mo implementazione di un ring buffer in NOR flash
Ùn ci hè bisognu di almacenà a lunghezza o altre informazioni nantu à u locu di e dati in l'intestazione; i marcatori chì indicanu i limiti di i registri sò abbastanza. Tuttavia, i dati anu da esse trattatu quandu scrive / leghje.
Aduprà 0xff cum'è un marcatore (chì riempia a pagina dopu a sguassate), cusì l'area libera ùn serà micca definitamente trattata cum'è dati.

Tabella di paragone:

Opzione 1
Opzione 2
Opzione 3

Tolleranza à l'errore
-
+
+

Compacità
+
-
+

A cumplessità di l'implementazione
*
**
**

L'opzione 1 hà un difettu fatale: se qualcunu di l'intestazione hè dannatu, tutta a catena successiva hè distrutta. L'opzioni rimanenti permettenu di ricuperà qualchi dati ancu in casu di danni massivu.
Ma quì hè apprupriatu à ricurdà chì avemu decisu di almacenà e dati in una forma compressa, è cusì perdemu tutti i dati nantu à a pagina dopu un registru "rottu", perchè ancu s'ellu ci hè un minus in a tavula, ùn avemu micca. piglià in contu.

Compattezza:

  • in a prima opzione, avemu bisognu di almacenà solu a lunghezza in l'intestazione; se usemu interi di lunghezza variabile, allora in a maiò parte di i casi pudemu fà cun un byte;
  • in a seconda opzione avemu bisognu di guardà l'indirizzu di partenza è a durata; u registru deve esse una dimensione custanti, aghju stimatu 4 bytes per record (dui bytes per l'offset, è dui bytes per a durata);
  • a terza opzione hà solu bisognu di un caratteru per indicà l'iniziu di a registrazione, più a registrazione stessa aumenterà da 1-2% per via di a scherma. In generale, circa parità cù a prima opzione.

Inizialmente, aghju cunsideratu a seconda opzione cum'è a principale (è ancu hà scrittu l'implementazione). Aghju abbandunatu solu quandu aghju finalmente decisu di utilizà a compressione.

Forse un ghjornu ancu aduprà una opzione simile. Per esempiu, se aghju da trattà cù u almacenamentu di dati per una nave chì viaghja trà a Terra è Marte, ci saranu esigenze completamente diverse per a affidabilità, a radiazione còsmica, ...

In quantu à a terza opzione: l'aghju datu duie stelle per a difficultà di implementazione solu perchè ùn mi piace micca scherzà cù schermi, cambià a durata in u prucessu, etc. Iè, forsi sò preghjudiziu, ma aghju da scrive u codice - perchè furzà à fà qualcosa chì ùn ti piace micca.

Riassuntu: Scegliemu l'opzione di almacenamento in forma di catene "header with length - data of variable length" per via di efficienza è facilità di implementazione.

Utilizà Campi di Bit per Monitorà u Successu di Operazioni di Scrittura

Ùn mi ricordu micca avà induve aghju avutu l'idea, ma pare qualcosa cusì:
Per ogni entrata, assignemu parechji bits per almacenà bandiere.
Comu avemu dettu prima, dopu à sguassate tutti i bit sò pieni di 1s, è pudemu cambià da 1 à 0, ma micca vice versa. Allora per "a bandiera ùn hè micca stabilita" usemu 1, per "a bandiera hè stallata" usemu 0.

Eccu ciò chì mette un registru di lunghezza variabile in u flash puderia vede:

  1. Pone a bandiera "a registrazione di a lunghezza hà iniziatu";
  2. Registrate a durata;
  3. Pone a bandiera "a registrazione di dati hà iniziatu";
  4. Avemu arregistramentu dati;
  5. Pone a bandiera "arregistramentu finitu".

Inoltre, averemu una bandiera "errore accadutu", per un totale di bandieri di 4 bit.

In questu casu, avemu dui stati stabili "1111" - a registrazione ùn hà micca iniziatu è "1000" - a registrazione hè stata successu; in casu di una interruzzione inespettata di u prucessu di registrazione, riceveremu stati intermedii, chì pudemu dopu detectà è processà.

L'approcciu hè interessante, ma solu prutege contr'à l'interruzioni di l'energia brusca è i fallimenti simili, chì, sicuru, hè impurtante, ma questu hè luntanu da l'unicu (o ancu u principale) mutivu di pussibuli fallimenti.

Riassuntu: Andemu avanti in cerca di una bona suluzione.

Checksums

I checksums permettenu ancu di assicurà (cun ​​​​una probabilità raghjone) chì leghjemu esattamente ciò chì duverebbe esse scrittu. È, à u cuntrariu di i campi di bit discututi sopra, sempre travaglianu.

Se avemu cunsideratu a lista di fonti putenziali di prublemi chì avemu discututu sopra, allura u checksum hè capaci di ricunnosce un errore indipendentemente da a so origine. (eccettu, forse, per i stranieri maliziusi - ponu falsificà ancu u checksum).

Allora se u nostru scopu hè di verificà chì e dati sò intactu, i checksums sò una grande idea.

L'scelta di l'algoritmu per u calculu di u checksum ùn hà micca suscitatu dumande - CRC. Da una banda, e proprietà matematiche permettenu di catturà certi tipi d'errore 100%; da l'altra banda, nantu à dati aleatoriu, stu algoritmu di solitu mostra a probabilità di scontri micca assai più grande di u limitu teoricu. A mo implementazione di un ring buffer in NOR flash. Pò esse micca l'algoritmu più veloce, nè hè sempre u minimu in quantu à u numeru di scontri, ma hà una qualità assai impurtante: in i testi chì aghju scontru, ùn ci era micca mudelli in quale chjaramente hà fiascatu. A stabilità hè a qualità principale in questu casu.

Esempiu di studiu volumetricu: parte di 1, parte di 2 (ligami à narod.ru, scusate).

Tuttavia, u compitu di selezziunà un checksum ùn hè micca cumpletu; CRC hè una famiglia sana di checksums. Avete bisognu di decide nantu à a lunghezza, è poi sceglie un polinomiu.

A scelta di a lunghezza di checksum ùn hè micca cusì simplice una quistione cum'è pare à u primu sguardu.

Lasciami illustrà:
Avemu a probabilità di un errore in ogni byte A mo implementazione di un ring buffer in NOR flash è un checksum ideale, calculemu u numeru mediu d'errori per milione di registri:

Dati, byte
Checksum, byte
Errori micca rilevati
Falsi rilevazioni di errore
Total falsi pusitivi

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

Sembra chì tuttu hè simplice - secondu a durata di e dati chì sò prutetti, sceglite a durata di u checksum cù un minimu di pusitivi incorrecte - è u truccu hè in u saccu.

In ogni casu, un prublema nasce cù checksums brevi: ancu s'elli sò boni à detectà errori unicu bit, ponu cun una probabilità abbastanza alta accettà dati cumplettamente casuali cum'è curretti. Ci era digià un articulu nantu à Habré chì descrive prublema in a vita vera.

Dunque, per fà una partita di checksum aleatoriu quasi impussibile, avete bisognu di utilizà checksums chì sò 32 bits o più longu. (per lunghezze più grande di 64 bit, e funzioni di hash criptografiche sò tipicamente usate).

Malgradu u fattu chì aghju scrittu prima chì avemu bisognu di risparmià spaziu per tutti i mezi, avemu sempre aduprà un checksum di 32-bit (16 bits ùn sò micca abbastanza, a probabilità di una collisione hè più di 0.01%; è 24 bits, cum'è elli). dì, ùn sò nè quì nè quì).

Una obiezione pò esse quì: avemu salvatu ogni byte quandu sceglite a compressione per dà avà 4 byte à una volta? Ùn saria megliu micca cumpressà o aghjunghje un checksum ? Di sicuru micca, senza cumpressione ùn significa micca, chì ùn avemu micca bisognu di cuntrollu di integrità.

Quandu sceglite un polinomiu, ùn avemu micca reinventatu a rota, ma pigliate u CRC-32C oghji populari.
Stu codice rileva errori di 6 bit nantu à pacchetti finu à 22 byte (forse u casu più cumuni per noi), errori di 4 bit in pacchetti finu à 655 bytes (ancu un casu cumuni per noi), 2 o qualsiasi nùmeru imparu di errori di bit nantu à i pacchetti di ogni lunghezza raghjone.

Se qualchissia hè interessatu à i dettagli

Articulu di Wikipedia circa CRC.

Paràmetri di codice crc-32c nantu U situ di Koopman - forsi u primu specialistu CRC in u pianeta.

В u so articuluun altru codice interessante, chì furnisce paràmetri ligeramente megliu per e lunghezze di pacchetti chì sò pertinenti per noi, ma ùn aghju micca cunsideratu a diffarenza significativa, è era abbastanza cumpetente per sceglie u codice persunalizatu invece di u standard è bè ricercatu.

Inoltre, postu chì i nostri dati sò cumpressi, a quistione hè: duvemu calculà u checksum di dati cumpressi o micca cumpressi?

Argumenti in favore di u calculu di u checksum di dati senza cumpressione:

  • In fine, avemu bisognu di verificà a sicurità di l'almacenamiento di dati - cusì cuntrollemu direttamente (à u stessu tempu, i pussibuli errori in l'implementazione di cumpressione / decompressione, danni causati da memoria rotta, etc. seranu verificati);
  • L'algoritmu di deflate in zlib hà una implementazione abbastanza matura è ùn deve micca caduta cù dati di input "storti"; in più, hè spessu capaci di detectà indipindentamente errori in u flussu di input, riducendu a probabilità generale di ùn rileva un errore (realizatu una prova cù l'inversione di un solu bit in un breve record, zlib hà rilevatu un errore. in circa un terzu di i casi).

Argumenti contru à u calculu di u checksum di dati micca cumpressi:

  • CRC hè "adattatu" specificamente per i pochi errori di bit chì sò caratteristiche di a memoria flash (un errore di pocu in un flussu cumpressu pò causà un cambiamentu massiu in u flussu di output, nantu à quale, puramente teoricamente, pudemu "catturà" una collisione);
  • Ùn mi piace micca veramente l'idea di passà dati potenzialmente rotti à u decompressore, Chi sàcumu hà da reagisce.

In questu prughjettu, decisu di deviate da a pratica generalmente accettata di almacenà un checksum di dati senza cumpressione.

Riassuntu: Usemu CRC-32C, calculemu u checksum da i dati in a forma in quale sò scritti à flash (dopu à a compressione).

Redundanza

L'usu di a codificazione redundante ùn elimina micca, sicuru, a perdita di dati, ma pò esse significativamente (spessu da parechji ordini di grandezza) riduce a probabilità di perdita di dati irrecuperable.

Pudemu aduprà diversi tipi di redundancy per correggere l'errori.
I codici Hamming ponu correggere l'errore di un solu bit, i codici di caratteri Reed-Solomon, parechje copie di dati cumminati cù checksums, o codificazioni cum'è RAID-6 ponu aiutà à ricuperà e dati ancu in casu di corruzzione massiva.
Inizialmente, era impegnatu à l'usu generalizatu di codificazione resistente à l'errore, ma poi aghju realizatu chì prima avemu bisognu di avè una idea di quali errori vulemu prutege, è dopu sceglite a codificazione.

Avemu dettu prima chì l'errore deve esse catturatu u più prestu pussibule. À chì punti pudemu scuntrà errori?

  1. Unfinished recording (per una certa ragione à u mumentu di a registrazione l'energia hè stata spenta, u Raspberry si congelava, ...)
    Alas, in l'eventu di un tali errore, tuttu ciò chì resta hè di ignurà i registri invalidi è cunsiderà i dati persi;
  2. Scrivite l'errori (per una certa ragione, ciò chì era scrittu à a memoria flash ùn era micca ciò chì hè statu scrittu)
    Pudemu immediatamente detectà tali errori se facemu una prova di leghje immediatamente dopu a registrazione;
  3. Distorsione di dati in memoria durante u almacenamentu;
  4. Errori di lettura
    Per curregà, se u checksum ùn currisponde micca, hè abbastanza per ripetiri a lettura parechje volte.

Questu hè, solu l'errore di u terzu tipu (corruzzione spontanea di dati durante l'almacenamiento) ùn ponu esse corrette senza codificazione resistente à l'errore. Sembra chì tali errori sò sempre assai improbabili.

Riassuntu: hè statu decisu di abbandunà a codificazione redundante, ma se u funziunamentu mostra l'errore di sta decisione, poi torna à cunsiderà u prublema (cù statistiche digià accumulate nantu à i fallimenti, chì vi permettenu di sceglie u tipu ottimale di codificazione).

Прочее

Di sicuru, u furmatu di l'articulu ùn ci permette di ghjustificà ogni pocu in u furmatu (è a mo forza hè digià esaurita), cusì vi passaraghju brevemente nantu à certi punti chì ùn sò micca toccu prima.

  • Hè statu decisu di fà tutte e pagine "uguali"
    Vale à dì, ùn ci saranu micca pagine speciale cù metadata, fili separati, etc., ma invece un filu unicu chì riscrive tutte e pagine in turnu.
    Questu assicura ancu l'usura nantu à e pagine, nisun puntu unicu di fallimentu, è mi piace solu;
  • Hè imperativu di furnisce versioning di u furmatu.
    Un formatu senza un numeru di versione in l'intestazione hè male!
    Hè abbastanza per aghjunghje un campu cù un certu Magic Number (firma) à l'intestazione di a pagina, chì indicà a versione di u formatu utilizatu. (Ùn pensu micca chì in pratica ci saranu ancu una decina di elli);
  • Aduprate un intestazione di lunghezza variabile per i registri (di quale ci sò assai), pruvate à fà 1 byte longu in a maiò parte di i casi;
  • Per codificà a lunghezza di l'intestazione è a durata di a parte tagliata di u registru cumpressu, utilizate codici binari di lunghezza variabile.

Aiutatu assai generatore in linea Codici di Huffman. In pocu minuti, avemu pussutu selezziunà i codici di lunghezza variabile richiesti.

Descrizzione di u furmatu di almacenamiento di dati

Ordine di byte

I campi più grande di un byte sò almacenati in formatu big-endian (ordine di byte di rete), vale à dì, 0x1234 hè scrittu cum'è 0x12, 0x34.

Paginazione

Tutta a memoria flash hè divisa in pagine di uguali dimensione.

A dimensione predeterminata di a pagina hè 32Kb, ma micca più di 1/4 di a dimensione tutale di u chip di memoria (per un chip di 4MB, 128 pagine sò ottenute).

Ogni pagina guarda dati indipindentamente di l'altri (vale à dì, i dati nantu à una pagina ùn anu micca riferimentu à e dati in una altra pagina).

Tutte e pagine sò numerate in ordine naturali (in ordine crescente di l'indirizzi), cuminciendu cù u numeru 0 (a pagina zero cumencia à l'indirizzu 0, a prima pagina principia à 32Kb, a seconda pagina principia à 64Kb, etc.)

U chip di memoria hè utilizatu cum'è un buffer ciclicu (ring buffer), vale à dì, a prima scrittura và à a pagina numero 0, dopu à u numeru 1, ..., quandu riempia l'ultima pagina, un novu ciclu principia è a registrazione cuntinueghja da a pagina zero. .

Dentru a pagina

A mo implementazione di un ring buffer in NOR flash
À l'iniziu di a pagina, un capu di pagina di 4 byte hè almacenatu, dopu un checksum d'intestazione (CRC-32C), dopu i registri sò almacenati in u formatu "header, data, checksum".

U titulu di a pagina (verde bruttu in u diagramma) hè custituitu da:

  • campu Magic Number di dui byte (ancu un signu di a versione di furmatu)
    per a versione attuale di u formatu hè calculatu cum'è 0xed00 ⊕ номер страницы;
  • contatore di dui byte "Versione di pagina" (numeru di ciclu di riscrittura di memoria).

L'entrata in a pagina sò almacenate in forma cumpressa (l'algoritmu di deflate hè utilizatu). Tutti i registri nantu à una pagina sò cumpressi in un filu (un dizziunariu cumuni hè utilizatu), è nantu à ogni nova pagina a compressione principia di novu. Vale à dì, per decompressà qualsiasi registru, tutti i registri previ da questa pagina (è solu questu) sò richiesti.

Ogni registru serà cumpressu cù a bandiera Z_SYNC_FLUSH, è à a fine di u flussu cumpressu ci sarà 4 bytes 0x00, 0x00, 0xff, 0xff, possibbilmente preceduti da unu o dui byte più zero.
Scartemu sta sequenza (4, 5 o 6 bytes longu) quandu scrivite in a memoria flash.

L'intestazione di u record hè di 1, 2 o 3 byte chì almacenanu:

  • un bit (T) chì indica u tipu di registrazione: 0 - cuntestu, 1 - log;
  • un campu di lunghezza variabile (S) da 1 à 7 bit, chì definisce a lunghezza di l'intestazione è a "coda" chì deve esse aghjuntu à u record per a decompressione;
  • lunghezza record (L).

S table di valori:

S
Lunghezza di l'intestazione, byte
Scartata in scrittura, byte

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)

Aghju pruvatu à illustrà, ùn sò micca chjaramente hè risultatu:
A mo implementazione di un ring buffer in NOR flash
Giallu quì indica u campu T, biancu u campu S, verde L (a durata di i dati cumpressi in bytes), blu i dati cumpressi, rossi i bytes finali di i dati cumpressi chì ùn sò micca scritti in memoria flash.

Cusì, pudemu scrive intestazioni di registrazione di a lunghezza più cumuna (finu à 63 + 5 bytes in forma compressa) in un byte.

Dopu à ogni registru, un checksum CRC-32C hè almacenatu, in quale u valore invertitu di u checksum precedente hè utilizatu cum'è u valore iniziale (init).

CRC hà a pruprietà di "durata", a seguente formula funziona (più o minus inversione di bit in u prucessu): A mo implementazione di un ring buffer in NOR flash.
Hè, in fattu, calculemu u CRC di tutti i bytes previ di intestazioni è dati in questa pagina.

Direttamente dopu à u checksum hè l'intestazione di u prossimu record.

L'intestazione hè cuncepita in tale manera chì u so primu byte hè sempre diversu da 0x00 è 0xff (se invece di u primu byte di l'intestazione scontru 0xff, allora questu significa chì questu hè una zona inutilizata; 0x00 signala un errore).

Esempiu di algoritmi

Lettura da a memoria flash

Ogni lettura vene cun un cuntrollu di checksum.
Se u checksum ùn currisponde micca, a lettura hè ripetuta parechje volte in a speranza di leghje i dati curretti.

(questu hè sensu, Linux ùn cache micca leghje da NOR Flash, pruvatu)

Scrivite in a memoria flash

Ricordemu i dati.
Leghjemu.

Se i dati letti ùn currispondenu micca à i dati scritti, riempiemu l'area cù zeri è signalemu un errore.

Preparazione di un novu microcircuit per u funziunamentu

Per l'inizializazione, un capu cù a versione 1 hè scrittu à a prima pagina (o piuttostu zero).
Dopu questu, u cuntestu iniziale hè scrittu à sta pagina (cuntene l'UUID di a macchina è i paràmetri predeterminati).

Eccu, a memoria flash hè pronta per l'usu.

Caricà a macchina

Quandu si carica, i primi 8 bytes di ogni pagina (header + CRC) sò letti, e pagine cù un Magic Number scunnisciutu o un CRC incorrect sò ignorati.
Da e pagine "corrette", e pagine cù a versione massima sò selezziunate, è a pagina cù u numeru più altu hè presa da elli.
U primu registru hè lettu, a currezzione di u CRC è a presenza di a bandiera "cuntestu" sò verificati. Se tuttu va bè, sta pagina hè cunsiderata attuale. Se no, turnemu à u precedente finu à truvà una pagina "live".
è nantu à a pagina truvata avemu lettu tutti i registri, quelli chì avemu usatu cù a bandiera "cuntestu".
Salvà u dizziunariu zlib (sarà necessariu per aghjunghje à sta pagina).

Hè questu, u scaricamentu hè cumpletu, u cuntestu hè restauratu, pudete travaglià.

Adding a Journal Entry

Cumpressemu u registru cù u dizziunariu currettu, spicificendu Z_SYNC_FLUSH. Videmu s'ellu u registru cumpressu si adatta à a pagina attuale.
S'ellu ùn hè micca adattatu (o ci sò stati errori CRC nantu à a pagina), principià una nova pagina (vede sottu).
Scrivemu u record è CRC. Se si verifica un errore, inizià una nova pagina.

Nova pagina

Selezziunamu una pagina libera cù u numeru minimu (consideremu una pagina libera per esse una pagina cù un checksum incorrectu in l'intestazione o cù una versione menu di l'attuale). Se ùn ci sò micca tali pagine, selezziunate a pagina cù u numeru minimu da quelli chì anu una versione uguale à quella attuale.
Sguassemu a pagina selezziunata. Cuntrollamu u cuntenutu cù 0xff. Se qualcosa hè sbagliatu, pigliate a prossima pagina libera, etc.
Scrivemu un capu nantu à a pagina sguassata, a prima entrata hè u statu attuale di u cuntestu, u prossimu hè l'entrata di logu micca scrittu (se ci hè unu).

Applicabilità di u furmatu

In u mo parè, hè diventatu un bonu formatu per almacenà ogni flussu d'infurmazioni più o menu comprimibili (testu chjaru, JSON, MessagePack, CBOR, possibbilmente protobuf) in NOR Flash.

Di sicuru, u formatu hè "su misura" per SLC NOR Flash.

Ùn deve esse usatu cù media BER altu cum'è NAND o MLC NOR (Hè ancu una tale memoria dispunibule per a vendita? L'aghju vistu solu in l'opere nantu à i codici di correzione).

Inoltre, ùn deve esse usatu cù i dispositi chì anu u so propiu FTL: USB flash, SD, MicroSD, etc (per una tale memoria aghju creatu un furmatu cù una dimensione di pagina di 512 bytes, una firma à l'iniziu di ogni pagina è numeri unichi di registrazione - qualchì volta era pussibule di ricuperà tutte e dati da una unità flash "glitched" per una lettura sequenziale simplice).

Sicondu i travaglii, u formatu pò esse usatu senza cambiamenti in unità flash da 128Kbit (16Kb) à 1Gbit (128MB). Se vulete, pudete aduprà nantu à chips più grande, ma probabilmente avete bisognu di aghjustà a dimensione di a pagina (Ma quì a quistione di a fattibilità ecunomica si pone digià; u prezzu per un grande voluminu NOR Flash ùn hè micca incuragisce).

Se qualchissia trova u formatu interessante è vole usà in un prughjettu apertu, scrivite, pruvaraghju à truvà u tempu, pulisce u codice è postu in github.

cunchiusioni

Comu pudete vede, à a fine, u furmatu hè diventatu simplice è ancu noioso.

Hè difficiuli di riflette l'evoluzione di u mo puntu di vista in un articulu, ma crede à mè: inizialmente vulia creà qualcosa di sufisticatu, indestructible, capace di sopravvive ancu à una splusione nucleare in prossimità. Tuttavia, a raghjoni (speru) hà ancu vintu è pocu à pocu e priorità si sò spustate versu a simplicità è a compattezza.

Puderia esse ch'e aghju sbagliatu ? Iè, sicuru. Puderia esse bè, per esempiu, chì avemu compru un batch di microcircuits di bassa qualità. O per una altra ragione, l'equipaggiu ùn risponde micca à l'aspettattivi di affidabilità.

Aghju un pianu per questu? Pensu chì dopu avè lettu l'articulu ùn avete micca dubbitu chì ci hè un pianu. È mancu solu.

In una nota un pocu più seriu, u furmatu hè statu sviluppatu cum'è una opzione di travagliu è cum'è un "pallone di prova".

À u mumentu tuttu nantu à a tavula funziona bè, literalmente l'altru ghjornu a suluzione serà implementata (circa) nantu à centinaie di dispusitivi, andemu à vede ciò chì succede in u funziunamentu di "combattimentu" (per furtuna, spergu chì u furmatu vi permette di scopre in modu affidabile i fallimenti; cusì pudete cullà statistiche cumplete). In uni pochi di mesi serà pussibule di piglià cunclusioni (è s'è vo site sfurtunatu, ancu prima).

Se, basatu annantu à i risultati di l'usu, i prublemi serii sò scuperti è i migliori sò necessarii, allora scriveraghju definitivamente.

Letteratura

Ùn vulia micca fà una longa lista tediosa di opere usate; dopu tuttu, tutti anu Google.

Quì aghju decisu di lascià una lista di scuperte chì mi parevanu particularmente interessanti, ma à pocu à pocu sò migrati direttamente in u testu di l'articulu, è un articulu ferma in a lista:

  1. Utilità infgen da l'autore zlib. Pò vede chjaramente u cuntenutu di l'archivi deflate/zlib/gzip. Sè avete da trattà cù a struttura interna di u formatu deflate (o gzip), vi cunsigliu assai.

Source: www.habr.com

Add a comment