{"id":53751,"date":"2019-12-09T00:00:00","date_gmt":"2019-12-08T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash"},"modified":"2020-02-18T14:01:41","modified_gmt":"2020-02-18T11:01:41","slug":"moya-realizatsiya-koltsevogo-bufera-v-nor-flash","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","title":{"rendered":"La mia implementazione di un buffer circolare nella NOR flash","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h1 id=\"predystoriya\">Contesto<\/h1>\n<p><\/p>\n<p>Abbiamo distributori automatici di nostra produzione. All'interno ci sono Raspberry Pi e un po' di componentistica su una scheda separata. Sono collegati un ripetitore di monete, un accettatore di banconote e un terminale bancario\u2026 Tutto \u00e8 gestito da un programma sviluppato internamente. L'intera storia operativa viene registrata in un diario su una chiavetta USB (MicroSD), che poi viene trasmessa via internet (tramite un modem USB) a un server, dove viene salvata in un database. Le informazioni sulle vendite vengono caricate in 1C, e c'\u00e8 anche una semplice interfaccia web per il monitoraggio e simili. <\/p>\n<p><\/p>\n<p>Quindi il diario \u00e8 vitale \u2014 per la contabilit\u00e0 (qui ci sono i ricavi, le vendite, ecc.), per il monitoraggio (tutte le possibili anomalie e altre circostanze impreviste); questa \u00e8, possiamo dire, l'unica informazione che abbiamo su questo distributore. <\/p>\n<p><\/p>\n<h1 id=\"problema\">Problema<\/h1>\n<p><\/p>\n<p>Le chiavette USB si rivelano dispositivi molto inaffidabili. Si rompono con sorprendente regolarit\u00e0. Questo porta sia a fermi dei distributori, sia (se per qualche motivo il diario non pu\u00f2 essere inviato online) a perdite di dati.<\/p>\n<p><\/p>\n<p><em>Questa non \u00e8 la prima esperienza con le chiavette USB, in precedenza c'era un altro progetto con pi\u00f9 di cento dispositivi, dove il diario era salvato su chiavette USB; anche l\u00ec ci sono stati problemi di affidabilit\u00e0, talvolta il numero di chiavette rotte in un mese si contava a decine. Abbiamo provato diverse chiavette, incluse quelle di marca con memoria SLC; alcune modelli sono pi\u00f9 affidabili di altre, ma la sostituzione delle chiavette non ha risolto radicalmente il problema.<\/em><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <\/p>\n<p><strong>Attenzione!<\/strong> \u041b\u043e\u043d\u0433\u0440\u0438\u0434! \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0435\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e &#171;\u043f\u043e\u0447\u0435\u043c\u0443&#187;, \u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e &#171;\u043a\u0430\u043a&#187;, \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0440\u0430\u0437\u0443 \u0438\u0434\u0442\u0438 <noindex><a rel=\"nofollow\" href=\"#format\">in fondo<\/a><\/noindex> dell'articolo.<\/p>\n<p><\/p>\n<h1 id=\"reshenie\">Soluzione<\/h1>\n<p><\/p>\n<p>La prima cosa che mi viene in mente: rinunciare a MicroSD, installare, ad esempio, un SSD e avviarlo da l\u00ec. Teoricamente \u00e8 possibile, probabilmente, ma relativamente costoso e non cos\u00ec affidabile (si aggiunge un adattatore USB-SATA; anche le statistiche sui SSD economici non sono incoraggianti).<\/p>\n<p><\/p>\n<p>Dischi USB HDD non sembrano una soluzione particolarmente attraente.<\/p>\n<p><\/p>\n<p>Quindi siamo giunti a questa opzione: mantenere l'avvio da MicroSD, ma usarli in modalit\u00e0 di sola lettura, e conservare il diario di funzionamento (e altre informazioni uniche per ciascun dispositivo \u2014 numero di serie, calibrazioni dei sensori, ecc.) da un'altra parte. <\/p>\n<p><\/p>\n<p>L'argomento dei filesystem in sola lettura per Raspberry Pi \u00e8 stato gi\u00e0 esplorato a fondo, non mi soffermer\u00f2 sui dettagli di implementazione in questo articolo. <em>(ma se ci sar\u00e0 interesse, magari scriver\u00f2 un mini-articolo su questo tema)<\/em>. L'unico punto che vorrei sottolineare: sia per esperienza personale, sia per le recensioni di chi gi\u00e0 ha implementato, il guadagno in affidabilit\u00e0 \u00e8 reale. S\u00ec, \u00e8 impossibile eliminare completamente i guasti, tuttavia \u00e8 assolutamente possibile ridurne significativamente la frequenza. Inoltre, le schede diventano unificate, il che semplifica notevolmente la sostituzione per il personale di servizio.<\/p>\n<p><\/p>\n<h2 id=\"apparatnaya-chast\">Parte hardware<\/h2>\n<p><\/p>\n<p>Non c'erano dubbi sulla scelta del tipo di memoria \u2014 NOR Flash.<br \/>\nArgomenti: <\/p>\n<p><\/p>\n<ul>\n<li>\u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 (\u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0448\u0438\u043d\u0430 SPI, \u043e\u043f\u044b\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c, \u0442\u0430\u043a \u0447\u0442\u043e &#171;\u0436\u0435\u043b\u0435\u0437\u043d\u044b\u0445&#187; \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u043d\u0435 \u043f\u0440\u0435\u0434\u0432\u0438\u0434\u0438\u0442\u0441\u044f);<\/li>\n<li>prezzo ridicolo;<\/li>\n<li>protocollo standard di funzionamento (l'implementazione \u00e8 gi\u00e0 presente nel kernel di Linux, se vuoi puoi prendere una soluzione esterna, che ci sono, o addirittura scrivere la tua, dato che \u00e8 tutto molto semplice);<\/li>\n<li>affidabilit\u00e0 e durata:<br \/>\nda un tipico datasheet: i dati vengono conservati per 20 anni, 100000 cicli di cancellazione per ogni blocco;<br \/>\nda fonti esterne: BER estremamente basso, si postula l'assenza della necessit\u00e0 di codici di correzione degli errori <em>(in alcuni lavori si considera ECC per NOR, ma di solito si intende MLC NOR, pu\u00f2 capitare anche questo)<\/em>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Facciamo alcune stime sui requisiti di volume e risorsa.<\/p>\n<p><\/p>\n<p>Vogliamo garantire che i dati vengano salvati per alcuni giorni. Questo \u00e8 necessario affinch\u00e9, in caso di problemi di connessione, la storia delle vendite non venga persa. Ci orienteremo su 5 giorni, in questo lasso di tempo <em>(anche considerando i fine settimana e le festivit\u00e0)<\/em> pu\u00f2 risolvere il problema.<\/p>\n<p><\/p>\n<p>Attualmente accumuliamo circa 100 kb di diario al giorno (3-4 mila registrazioni), tuttavia questa cifra sta crescendo \u2014 aumenta la dettagliabilit\u00e0, vengono aggiunti nuovi eventi. Inoltre, a volte ci sono picchi (un certo sensore inizia a inviare falsi allarmi, ad esempio). Calcoliamo su 10 mila registrazioni di 100 byte \u2014 un megabyte al giorno.<\/p>\n<p><\/p>\n<p>In totale risultano 5 MB di dati 'puliti' (ben comprimibili). A questi aggiungiamo <em>(una stima grossolana)<\/em> 1 MB di dati di servizio.<\/p>\n<p><\/p>\n<p>Quindi abbiamo bisogno di un chip da 8 MB se non utilizziamo la compressione, o 4 MB se la utilizziamo. Sono cifre molto realistiche per questo tipo di memoria.<\/p>\n<p><\/p>\n<p>Per quanto riguarda la risorsa: se prevediamo che la memoria sar\u00e0 completamente riscritta non pi\u00f9 spesso di una volta ogni 5 giorni, dopo 10 anni di servizio otteniamo meno di mille cicli di riscrittura.<br \/>\nRicordo che il produttore promette centomila.<\/p>\n<p>\n<b class=\"spoiler_title\">Un po' su NOR vs NAND<\/b><\/p>\n<p>Oggi, ovviamente, la memoria NAND \u00e8 molto pi\u00f9 popolare, ma per questo progetto non la utilizzerei: a differenza della NOR, la NAND richiede necessariamente l'uso di codici di correzione degli errori, tabelle dei blocchi difettosi, ecc., e i pin dei chip NAND sono tipicamente molto pi\u00f9 numerosi.<\/p>\n<p><\/p>\n<p>Tra i difetti della NOR possiamo citare:<\/p>\n<p><\/p>\n<ul>\n<li>basso volume (e, di conseguenza, alto costo per megabyte);<\/li>\n<li>velocit\u00e0 di trasferimento non elevata (in gran parte a causa dell'utilizzo di interfacce seriali, generalmente SPI o I2C);<\/li>\n<li>veloce cancellazione (a seconda delle dimensioni del blocco, richiede frazioni di secondo fino a diversi secondi).<\/li>\n<\/ul>\n<p><\/p>\n<p>Non sembra ci siano criticit\u00e0 per noi, quindi continuiamo.<\/p>\n<p><\/p>\n<p>Se sei interessato ai dettagli, \u00e8 stato scelto il chip <noindex><a rel=\"nofollow\" href=\"https:\/\/www.adestotech.com\/wp-content\/uploads\/doc3686.pdf\">at25df321a<\/a><\/noindex> <em>(comunque non \u00e8 rilevante, ci sono molte alternative sul mercato compatibili per pinout e sistema di comandi; anche se volessimo installare un chip di un altro produttore e\/o di un'altra capacit\u00e0, funzioner\u00e0 senza modifiche al codice)<\/em>.<\/p>\n<p><\/p>\n<p>Utilizzo il driver incorporato nel kernel di Linux, su Raspberry, grazie al supporto per device tree overlay, \u00e8 tutto molto semplice: basta inserire l'overlay compilato in \/boot\/overlays e modificare leggermente \/boot\/config.txt.<\/p>\n<p>\n<b class=\"spoiler_title\">Esempio di file dts<\/b><\/p>\n<p>A dire il vero, non sono sicuro che sia scritto senza errori, ma funziona.<\/p>\n<p><\/p>\n<pre><code class=\"plaintext\">\/*\n * Device tree overlay for at25 at spi0.1\n *\/\n\n\/dts-v1\/;\n\/plugin\/;\n\n\/ {\n    compatible = \"brcm,bcm2835\", \"brcm,bcm2836\", \"brcm,bcm2708\", \"brcm,bcm2709\"; \n\n    \/* disable spi-dev for spi0.1 *\/\n    fragment@0 {\n        target = &lt;&amp;spi0&gt;;\n        __overlay__ {\n            status = \"okay\";\n            spidev@1{\n                status = \"disabled\";\n            };\n        };\n    };\n\n    \/* the spi config of the at25 *\/\n    fragment@1 {\n        target = &lt;&amp;spi0&gt;;\n        __overlay__ {\n            #address-cells = &lt;1&gt;;\n            #size-cells = &lt;0&gt;;\n            flash: m25p80@1 {\n                    compatible = \"atmel,at25df321a\";\n                    reg = &lt;1&gt;;\n                    spi-max-frequency = &lt;50000000&gt;;\n\n                    \/* default to false:\n                    m25p,fast-read ;\n                    *\/\n            };\n        };\n    };\n\n    __overrides__ {\n        spimaxfrequency = &lt;&amp;flash&gt;,\"spi-max-frequency:0\";\n        fastread = &lt;&amp;flash&gt;,\"m25p,fast-read?\";\n    };\n};<\/code><\/pre>\n<p>\n<b class=\"spoiler_title\">E un'altra riga in config.txt<\/b><\/p>\n<pre><code class=\"plaintext\">dtoverlay=at25:spimaxfrequency=50000000<\/code><\/pre>\n<p><\/p>\n<p>Non descriver\u00f2 il collegamento del chip al Raspberry Pi. Da un lato, non sono un esperto di elettronica, dall'altro \u2014 qui \u00e8 tutto banale anche per me: il chip ha solo 8 pin, di cui abbiamo bisogno di terra, alimentazione, SPI (CS, SI, SO, SCK); i livelli corrispondono a quelli del Raspberry Pi, non \u00e8 necessaria alcuna ulteriore circuitazione \u2014 basta collegare i 6 contatti indicati.<\/p>\n<p><\/p>\n<h2 id=\"postanovka-zadachi\">Definizione del compito<\/h2>\n<p><\/p>\n<p>Come al solito, la definizione del compito avviene in diverse iterazioni; mi sembra che sia giunto il momento per un'altra. Quindi, fermiamoci, mettiamo insieme ci\u00f2 che \u00e8 gi\u00e0 stato scritto e chiarifichiamo i dettagli rimasti nell'ombra.<\/p>\n<p><\/p>\n<p>Quindi, abbiamo deciso che il registro sar\u00e0 memorizzato nella Flash SPI NOR.<\/p>\n<p>\n<b class=\"spoiler_title\">Che cos'\u00e8 la Flash NOR per chi non lo sa<\/b><\/p>\n<p>\u00c8 una memoria non volatile, sulla quale possiamo eseguire tre operazioni:<\/p>\n<p><\/p>\n<ol>\n<li>Lettura:<br \/>\nLa lettura pi\u00f9 comune: forniamo l'indirizzo e leggiamo il numero di byte di cui abbiamo bisogno;<\/li>\n<li>Scrittura:<br \/>\nLa scrittura nella flash NOR sembra normale, ma ha una particolarit\u00e0: possiamo solo cambiare 1 in 0, ma non viceversa. Ad esempio, se nella cella di memoria c'era 0x55, dopo la scrittura di 0x0f ci sar\u00e0 0x05 <em>(vedi tabella qui sotto)<\/em>;<\/li>\n<li>Cancellazione:<br \/>\nNaturalmente, dobbiamo anche essere in grado di eseguire l'operazione inversa \u2014 cambiare 0 in 1, ed \u00e8 proprio per questo che esiste l'operazione di cancellazione. A differenza delle prime due, opera non su byte, ma su blocchi (il blocco minimo di cancellazione nel chip selezionato \u00e8 di 4kb). La cancellazione annienta l'intero blocco e questo \u00e8 l'unico modo per cambiare 0 in 1. Pertanto, quando si lavora con la memoria flash, spesso \u00e8 necessario allineare le strutture dati al confine del blocco di cancellazione.<br \/>\nScrittura nella Flash NOR:<\/li>\n<\/ol>\n<p><\/p>\n<p>Dati binari<\/p>\n<p><strong>C'era<\/strong><br \/>\n<code>01010101<\/code><\/p>\n<p><strong>Scritti.<\/strong><br \/>\n<code>00001111<\/code><\/p>\n<p><strong>Diventato<\/strong><br \/>\n<code>00000101<\/code><\/p>\n<p><\/p>\n<p>Il registro stesso rappresenta una sequenza di registrazioni di lunghezza variabile. La lunghezza tipica di una registrazione \u00e8 di circa 30 byte (anche se a volte ci sono registrazioni di diversi kilobyte). <em>In questo caso, lavoriamo con esse semplicemente come a un insieme di byte, ma se interessa, all'interno delle registrazioni si utilizza CBOR.<\/em><\/p>\n<p><\/p>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u0436\u0443\u0440\u043d\u0430\u043b\u0430, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e &#171;\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u0447\u043d\u0443\u044e&#187; \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u043a\u0430\u043a \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c\u0443\u044e, \u0442\u0430\u043a \u0438 \u043d\u0435\u0442: \u043d\u0435\u043a\u0438\u0439 ID \u0430\u043f\u043f\u0430\u0440\u0430\u0442\u0430, \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432, \u0444\u043b\u0430\u0433 &#171;\u0430\u043f\u043f\u0430\u0440\u0430\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d&#187;, etc.<br \/>\nQueste informazioni consistono in una serie di registrazioni chiave-valore, anch'esse memorizzate in CBOR. Non abbiamo molte di queste informazioni (al massimo pochi kilobyte), e vengono aggiornate di rado.<br \/>\nIn futuro le chiameremo contesto.<\/p>\n<p><\/p>\n<p>Se ricordiamo da dove \u00e8 iniziato questo articolo, \u00e8 molto importante garantire l'affidabilit\u00e0 della memorizzazione dei dati e, se possibile, un funzionamento ininterrotto anche in caso di guasti hardware\/danni ai dati.<\/p>\n<p><\/p>\n<p>Quali fonti di problemi possiamo considerare?<\/p>\n<p><\/p>\n<ul>\n<li>\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 write\/erase. \u042d\u0442\u043e \u0438\u0437 \u0440\u0430\u0437\u0440\u044f\u0434\u0430 &#171;\u043f\u0440\u043e\u0442\u0438\u0432 \u043b\u043e\u043c\u0430 \u043d\u0435\u0442 \u043f\u0440\u0438\u0451\u043c\u0430&#187;.<br \/>\nInformazioni da <noindex><a rel=\"nofollow\" href=\"https:\/\/electronics.stackexchange.com\/questions\/225956\/what-would-happen-in-case-of-power-outage-during-nor-flash-erase-or-programming\">dibattito<\/a><\/noindex> \u043d\u0430 stackexchange: \u043f\u0440\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u043f\u0438\u0442\u0430\u043d\u0438\u044f \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 flash \u0447\u0442\u043e erase (\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432 1), \u0447\u0442\u043e write (\u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0432 0) \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442 \u043a undefined behavior: \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f, \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e (\u0441\u043a\u0430\u0436\u0435\u043c, \u043c\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u043b\u0438 10 \u0431\u0430\u0439\u0442\/80 \u0431\u0438\u0442, \u0430 \u0443\u0441\u043f\u0435\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e 45 \u0431\u0438\u0442), \u043d\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u043e \u0438 \u0442\u043e, \u0447\u0442\u043e \u0447\u0430\u0441\u0442\u044c \u0431\u0438\u0442\u043e\u0432 \u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0432 &#171;\u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u043e\u043c&#187; \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 (\u0447\u0442\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0434\u0430\u0442\u044c \u043a\u0430\u043a 0, \u0442\u0430\u043a \u0438 1);<\/li>\n<li>Errori della memoria flash stessa.<br \/>\nIl BER, anche se molto basso, non pu\u00f2 essere zero;<\/li>\n<li>Errori sulla linea<br \/>\nI dati trasmessi via SPI non sono protetti in alcun modo; potrebbero verificarsi sia errori di bit singoli che errori di sincronizzazione \u2014 perdita o inserimento di bit (il che porta a gravi distorsioni dei dati);<\/li>\n<li>Altri errori\/fallimenti.<br \/>\n\u041e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043a\u043e\u0434\u0435, &#171;\u0433\u043b\u044e\u043a\u0438&#187; Raspberry, \u0432\u043c\u0435\u0448\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e \u0438\u043d\u043e\u043f\u043b\u0430\u043d\u0435\u0442\u044f\u043d&#8230;<\/li>\n<\/ul>\n<p><\/p>\n<p>Ho formulato i requisiti che, a mio avviso, sono necessari per garantire l'affidabilit\u00e0:<\/p>\n<p><\/p>\n<ul>\n<li>le registrazioni devono essere scritte nella memoria flash immediatamente, la scrittura ritardata non \u00e8 considerata; - se si \u00e8 verificato un errore, deve essere rilevato e gestito il prima possibile; - il sistema deve ripristinare il suo funzionamento dopo un errore, se possibile.<br \/>\n<em>(\u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0436\u0438\u0437\u043d\u0438 &#171;\u043a\u0430\u043a \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c&#187;, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c, \u0434\u0443\u043c\u0430\u044e, \u0432\u0441\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b\u0438\u0441\u044c: \u043f\u043e\u0441\u043b\u0435 \u0430\u0432\u0430\u0440\u0438\u0439\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 &#171;\u043f\u043e\u0431\u0438\u043b\u0430\u0441\u044c&#187; \u0444\u0430\u0439\u043b\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0438 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043d\u0435 \u0433\u0440\u0443\u0437\u0438\u0442\u0441\u044f)<\/em><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"idei-podhody-razmyshleniya\">Idee, approcci, riflessioni<\/h2>\n<p><\/p>\n<p>Quando ho iniziato a pensare a questo compito, ho avuto molte idee, ad esempio:<\/p>\n<p><\/p>\n<ul>\n<li>utilizzare la compressione dei dati;<\/li>\n<li>utilizzare strutture dati ingegnose, ad esempio memorizzare gli header delle registrazioni separatamente dalle stesse registrazioni, in modo che in caso di errore in una registrazione sia possibile leggere le altre senza problemi;<\/li>\n<li>utilizzare campi bit per controllare il completamento della registrazione in caso di interruzione di alimentazione;<\/li>\n<li>memorizzare checksum per tutto e per tutti;<\/li>\n<li>utilizzare qualche forma di codifica resistente alle interferenze.<\/li>\n<\/ul>\n<p><\/p>\n<p>Alcune di queste idee sono state utilizzate, mentre altre sono state scartate. Procediamo con ordine.<\/p>\n<p><\/p>\n<h3 id=\"szhatie-dannyh\">Compressione dei dati<\/h3>\n<p><\/p>\n<p>\u0421\u0430\u043c\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0444\u0438\u043a\u0441\u0438\u0440\u0443\u0435\u043c \u0432 \u0436\u0443\u0440\u043d\u0430\u043b\u0435, \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0435 \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0435\u043c\u044b\u0435 (&#171;\u043a\u0438\u043d\u0443\u043b\u0438 \u043c\u043e\u043d\u0435\u0442\u043a\u0443 5 \u0440\u0443\u0431\u043b\u0435\u0439&#187;, &#171;\u043d\u0430\u0436\u0430\u043b\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0432\u044b\u0434\u0430\u0447\u0438 \u0441\u0434\u0430\u0447\u0438&#187;, &#8230;). \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0436\u0430\u0442\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u043c.<\/p>\n<p><\/p>\n<p>I costi di compressione sono insignificanti (il nostro processore \u00e8 abbastanza potente, anche sul primo Pi c'era un core con frequenza di 700 MHz, e sui modelli attuali diversi core con frequenza superiore a un gigahertz), la velocit\u00e0 di scambio con il deposito non \u00e8 alta (alcuni megabyte al secondo), le dimensioni delle registrazioni non sono elevate. In generale, se la compressione avr\u00e0 un impatto sulle prestazioni, sar\u00e0 solo positivo <em>(assolutamente non critico, sto solo constatando)<\/em>. Inoltre, noi non utilizziamo un vero embedded, ma un normale Linux \u2014 quindi l'implementazione non dovrebbe richiedere molti sforzi (basta collegare una libreria e utilizzare alcune funzioni di essa).<\/p>\n<p><\/p>\n<p>\u00c8 stato preso un campione di log da un dispositivo funzionante (1,7 MB, 70.000 registrazioni) e inizialmente verificato per la compressibilit\u00e0 utilizzando gzip, lz4, lzop, bzip2, xz, zstd disponibili sul computer.<\/p>\n<p><\/p>\n<ul>\n<li>gzip, xz, zstd hanno mostrato risultati simili (40KB).<br \/>\n\u00c8 sorprendente che il trendy xz abbia mostrato risultati pari a gzip o zstd;<\/li>\n<li>lzip con impostazioni predefinite ha dato risultati leggermente peggiori;<\/li>\n<li>lz4 e lzop hanno mostrato risultati non molto buoni (150KB);<\/li>\n<li>bzip2 ha mostrato risultati sorprendentemente buoni (18KB).<\/li>\n<\/ul>\n<p><\/p>\n<p>Quindi, i dati si comprimono molto bene.<br \/>\nPertanto, (se non troviamo difetti fatali) sar\u00e0 necessario comprimere! Semplicemente perch\u00e9 sulla stessa memoria flash sar\u00e0 possibile memorizzare pi\u00f9 dati.<\/p>\n<p><\/p>\n<p>Pensiamo ai difetti.<\/p>\n<p><\/p>\n<p>Il primo problema: abbiamo gi\u00e0 concordato che ogni registrazione deve essere scritta immediatamente nella flash. Di solito, il compressore raccoglie i dati dal flusso di input fino a quando non decide che \u00e8 ora di scrivere nell'output. A noi serve invece ottenere subito un blocco di dati compressi e memorizzarlo nella memoria non volatile.<\/p>\n<p><\/p>\n<p>Vedo tre strade:<\/p>\n<p><\/p>\n<ol>\n<li>Comprimere ogni registrazione usando la compressione a dizionario al posto degli algoritmi considerati sopra.<br \/>\n\u0412\u043f\u043e\u043b\u043d\u0435 \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u043d\u043e \u043c\u043d\u0435 \u043e\u043d \u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f. \u0414\u043b\u044f \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u0435\u0435-\u043c\u0435\u043d\u0435\u0435 \u043f\u0440\u0438\u043b\u0438\u0447\u043d\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0441\u0436\u0430\u0442\u0438\u044f \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c &#171;\u0437\u0430\u0442\u043e\u0447\u0435\u043d&#187; \u043f\u043e\u0434 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043b\u044e\u0431\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u0442 \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u0441\u0436\u0430\u0442\u0438\u044f \u043a\u0430\u0442\u0430\u0441\u0442\u0440\u043e\u0444\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0430\u0434\u0430\u0435\u0442. \u0414\u0430, \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u043d\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044f, \u043d\u043e \u044d\u0442\u043e \u0436\u0435 \u0433\u043e\u043b\u043e\u0432\u043d\u0430\u044f \u0431\u043e\u043b\u044c \u2014 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044f; \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441 \u043a\u0430\u043a\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u0441\u043b\u043e\u0432\u0430\u0440\u044f \u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0441\u0436\u0430\u0442\u0430&#8230;<\/li>\n<li>\u0421\u0436\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c &#171;\u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u043c\u0438&#187; \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430\u043c\u0438, \u043d\u043e \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445.<br \/>\nGli algoritmi di compressione considerati non sono progettati per lavorare con registrazioni di queste dimensioni (decine di byte), il fattore di compressione sar\u00e0 chiaramente inferiore a 1 (ossia incremento del volume dei dati anzich\u00e9 compressione);<\/li>\n<li>Fare FLUSH dopo ogni registrazione.<br \/>\nMolte librerie di compressione supportano il FLUSH. Questo \u00e8 un comando (o parametro per la procedura di compressione), ricevuto il quale, il compressore genera un flusso compresso in modo tale da poter ripristinare <strong>tutti<\/strong> i dati non compressi che sono gi\u00e0 stati ricevuti. Questa \u00e8 un'analogia <code>sync<\/code> nei file system o <code>commit<\/code> in sql.<br \/>\n\u00c8 importante che le operazioni di compressione successive possano utilizzare il dizionario accumulato e il grado di compressione non sar\u00e0 colpito cos\u00ec duramente come nella precedente opzione.<\/li>\n<\/ol>\n<p><\/p>\n<p>Penso sia ovvio che ho scelto la terza opzione, approfondiamo questo punto.<\/p>\n<p><\/p>\n<p>Trovato <noindex><a rel=\"nofollow\" href=\"https:\/\/www.bolet.org\/~pornin\/deflate-flush.html\">ottimo articolo<\/a><\/noindex> su FLUSH in zlib.<\/p>\n<p><\/p>\n<p>Ho realizzato un test ispirato a un articolo, raccogliendo 70.000 registri da un dispositivo reale, con una dimensione della pagina di 60 KB. <em>(parleremo ancora della dimensione della pagina)<\/em> ho ottenuto:<\/p>\n<p><\/p>\n<p>Dati di origine<br \/>\nCompressione gzip -9 (senza FLUSH)<br \/>\nzlib con Z_PARTIAL_FLUSH<br \/>\nzlib con Z_SYNC_FLUSH<\/p>\n<p><strong>Dimensione, KB<\/strong><br \/>\n1692<br \/>\n40<br \/>\n352<br \/>\n604<\/p>\n<p><\/p>\n<p>A prima vista, il costo del FLUSH sembra eccessivo, ma in realt\u00e0 abbiamo poche opzioni: o non comprimere affatto, o comprimere (e in modo molto efficace) con FLUSH. Non dimentichiamo che abbiamo 70.000 registri e l'overhead introdotto da Z_PARTIAL_FLUSH \u00e8 solo di 4-5 byte per registrazione. Il rapporto di compressione \u00e8 risultato essere quasi 5:1, un ottimo risultato.<\/p>\n<p>\n<b class=\"spoiler_title\">\u041c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u043c, \u043d\u043e \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 Z_SYNC_FLUSH &#8212; \u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u0435\u043b\u0430\u0442\u044c FLUSH<\/b><\/p>\n<p>Nel caso in cui si utilizzi Z_SYNC_FLUSH, gli ultimi 4 byte di ogni registrazione saranno sempre 0x00, 0x00, 0xff, 0xff. E se li conosciamo, possiamo non memorizzarli, quindi la dimensione finale \u00e8 di soli 324 KB.<\/p>\n<p><\/p>\n<p>Nell'articolo a cui mi riferisco, c'\u00e8 una spiegazione:<\/p>\n<p><\/p>\n<blockquote><p>Un nuovo blocco di tipo 0 con contenuti vuoti viene aggiunto.<\/p>\n<p>Un blocco di tipo 0 con contenuti vuoti consiste in:<\/p>\n<ul>\n<li>l'intestazione del blocco di tre bit;<\/li>\n<li>da 0 a 7 bit pari a zero, per raggiungere l'allineamento dei byte;<\/li>\n<li>la sequenza di quattro byte 00 00 FF FF.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Come si pu\u00f2 notare facilmente, nel blocco finale prima di questi 4 byte ci sono da 3 a 10 bit zero. Tuttavia, la pratica ha dimostrato che i bit zero sono in realt\u00e0 almeno 10.<\/p>\n<p><\/p>\n<p>Si scopre che blocchi di dati cos\u00ec brevi sono solitamente (sempre?) codificati usando un blocco di tipo 1 (blocco fisso), che deve necessariamente terminare con 7 bit zero, arrivando cos\u00ec a garantire da 10 a 17 bit zero (e gli altri saranno zero con una probabilit\u00e0 di circa il 50%).<\/p>\n<p><\/p>\n<p>Pertanto, nei dati di test, in 100% dei casi, prima di 0x00, 0x00, 0xff, 0xff c'\u00e8 un byte zero, e in pi\u00f9 di un terzo dei casi ci sono due byte zero. <em>(forse \u00e8 dovuto al fatto che utilizzo CBOR binario, e nel caso di JSON testuale si incontrerebbero pi\u00f9 frequentemente blocchi di tipo 2 - blocco dinamico, quindi ci sarebbero blocchi senza ulteriori byte zero prima di 0x00, 0x00, 0xff, 0xff).<\/em>.<\/p>\n<p><\/p>\n<p>In definitiva, con i dati di test disponibili, si pu\u00f2 scendere a meno di 250 KB di dati compressi.<\/p>\n<p><\/p>\n<p>Si possono risparmiare ancora un po' di byte, facendo un po' di giocoleria con i bit: ora stiamo ignorando la presenza di diversi bit zero alla fine del blocco, alcuni bit all'inizio del blocco non cambiano nemmeno...<br \/>\nMa qui ho preso la decisione di fermarmi, altrimenti a questo ritmo potrei finire per sviluppare un mio archiviatore.<\/p>\n<p><\/p>\n<p>In sintesi, dai miei dati di test ho ottenuto 3-4 byte per registrazione, e il rapporto di compressione \u00e8 stato superiore a 6:1. A dir la verit\u00e0: non mi aspettavo un risultato del genere, a mio parere tutto ci\u00f2 che \u00e8 meglio di 2:1 \u00e8 gi\u00e0 un risultato che giustifica l'uso della compressione.<\/p>\n<p><\/p>\n<p>Tutto bene, ma zlib (deflate) \u00e8 comunque un algoritmo di compressione piuttosto arcaico e un po' retro. Solo il fatto che venga utilizzato un dizionario delle ultime 32 KB del flusso non compresso sembra strano oggi (cio\u00e8 se un blocco di dati \u00e8 molto simile a ci\u00f2 che era nel flusso di ingresso 40 KB fa, inizier\u00e0 a essere archiviato di nuovo, invece di fare riferimento a un'entrata passata). Negli archivi moderni, la dimensione del dizionario \u00e8 pi\u00f9 spesso misurata in megabyte piuttosto che in kilobyte.<\/p>\n<p><\/p>\n<p>Quindi continuiamo la nostra mini-ricerca sugli archivi.<\/p>\n<p><\/p>\n<p>Il successivo test \u00e8 stato condotto su bzip2 (ricordo, senza FLUSH ha mostrato un grado fantastico di compressione, quasi 100:1). Purtroppo, con FLUSH ha mostrato prestazioni molto scarse, e la dimensione dei dati compressi \u00e8 risultata maggiore di quella dei dati non compressi.<\/p>\n<p>\n<b class=\"spoiler_title\">Le mie supposizioni sulle ragioni del fallimento.<\/b><\/p>\n<p>Libbz2 offre solo un'opzione di flush, che sembra pulire il dizionario (analogo a Z_FULL_FLUSH in zlib), e non possiamo parlare di una compressione efficiente dopo questo.<\/p>\n<p><\/p>\n<p>E infine, \u00e8 stato testato zstd. A seconda dei parametri, comprime al livello di gzip, ma molto pi\u00f9 velocemente, oppure meglio di gzip.<\/p>\n<p><\/p>\n<p>\u0423\u0432\u044b, \u0441 FLUSH \u0438 \u043e\u043d \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u0441\u0435\u0431\u044f &#171;\u043d\u0435 \u043e\u0447\u0435\u043d\u044c&#187;: \u0440\u0430\u0437\u043c\u0435\u0440 \u0441\u0436\u0430\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0448\u0435\u043b \u043e\u043a\u043e\u043b\u043e 700\u041a\u0431.<\/p>\n<p><\/p>\n<p>Io <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/facebook\/zstd\/issues\/900\">ho posto una domanda<\/a><\/noindex> sulla pagina del progetto su github, ho ricevuto la risposta che si pu\u00f2 prevedere di avere fino a 10 byte di dati di overhead per ogni blocco di dati compressi, che \u00e8 vicino ai risultati ottenuti, e non si potr\u00e0 mai raggiungere deflate.<\/p>\n<p><\/p>\n<p>A questo punto ho deciso di fermarmi con gli esperimenti sugli archivi (ricordo che xz, lzip, lzo, lz4 non hanno mostrato buone prestazioni nemmeno nella fase di test senza FLUSH, e non ho voluto considerare algoritmi di compressione pi\u00f9 esotici).<\/p>\n<p><\/p>\n<p>Torniamo ai problemi di archiviazione.<\/p>\n<p><\/p>\n<p>Il secondo (come si dice, in ordine ma non per importanza) problema \u00e8 che i dati compressi rappresentano un singolo flusso, nel quale ci sono costantemente riferimenti a segmenti precedenti. Pertanto, se un segmento dei dati compressi \u00e8 danneggiato, perdiamo non solo il blocco non compresso associato, ma anche tutti i successivi.<\/p>\n<p><\/p>\n<p>Ci sono approcci per risolvere questo problema:<\/p>\n<p><\/p>\n<ol>\n<li>Avvisare della comparsa di un problema significa aggiungere ridondanza nei dati compressi, che permette di identificare e correggere errori; di questo parleremo pi\u00f9 avanti;<\/li>\n<li>Minimizzare le conseguenze in caso di problema<br \/>\nAbbiamo gi\u00e0 detto in precedenza che ciascun blocco di dati pu\u00f2 essere compresso in modo indipendente, e in questo caso il problema scomparir\u00e0 da solo (la corruzione dei dati di un blocco comporter\u00e0 la perdita solo dei dati di quel blocco). Tuttavia, questo \u00e8 un caso estremo in cui la compressione dei dati sar\u00e0 inefficace. L'opposto estremo \u00e8 utilizzare tutti i 4MB del nostro chip come un'unica archiviazione, il che ci fornir\u00e0 un'ottima compressione, ma conseguenze catastrofiche in caso di corruzione dei dati.<br \/>\n<em>S\u00ec, \u00e8 necessario un compromesso in termini di affidabilit\u00e0. Ma bisogna ricordare che stiamo sviluppando un formato di archiviazione dei dati per una memoria non volatile con un BER estremamente basso e con una durata dichiarata di 20 anni.<\/em><\/li>\n<\/ol>\n<p><\/p>\n<p>Durante gli esperimenti ho scoperto che le perdite di livello di compressione cominciano a diventare pi\u00f9 o meno evidenti su blocchi di dati compressi di dimensione inferiore a 10KB.<br \/>\n\u0420\u0430\u043d\u0435\u0435 \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u043f\u0430\u043c\u044f\u0442\u044c \u0438\u043c\u0435\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u0443\u044e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044e, \u044f \u043d\u0435 \u0432\u0438\u0436\u0443 \u043f\u0440\u0438\u0447\u0438\u043d, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 &#171;\u043e\u0434\u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u2014 \u043e\u0434\u0438\u043d \u0431\u043b\u043e\u043a \u0441\u0436\u0430\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445&#187;.<\/p>\n<p><\/p>\n<p>Quindi, la dimensione minima ragionevole della pagina \u00e8 di 16KB (con un margine per le informazioni di servizio). Tuttavia, una dimensione cos\u00ec piccola della pagina impone limitazioni significative sulla dimensione massima del record.<\/p>\n<p><\/p>\n<p>Sebbene al momento non prevedo record superiori a un kilobyte in forma compressa, ho deciso di utilizzare pagine di dimensione 32KB (in totale 128 pagine per chip).<\/p>\n<p><\/p>\n<p><strong>Riepilogo:<\/strong><\/p>\n<p><\/p>\n<ul>\n<li>I dati sono archiviati compressi utilizzando zlib (deflate);<\/li>\n<li>Per ogni record impostiamo Z_SYNC_FLUSH;<\/li>\n<li>Per ogni record compresso, tagliamo i byte finali <em>(ad esempio, 0x00, 0x00, 0xff, 0xff)<\/em>; nell'intestazione indichiamo quanti byte abbiamo tagliato;<\/li>\n<li>I dati sono memorizzati in pagine da 32KB; all'interno della pagina c'\u00e8 un flusso continuo di dati compressi; su ogni pagina inizia una nuova compressione.<\/li>\n<\/ul>\n<p><\/p>\n<p>E, prima di concludere con la compressione, vorrei sottolineare che i dati compressi risultano in soli pochi byte per record, quindi \u00e8 estremamente importante non gonfiare le informazioni di servizio; ogni byte conta.<\/p>\n<p><\/p>\n<h3 id=\"hranenie-zagolovkov-dannyh\">Archiviazione delle intestazioni dei dati<\/h3>\n<p><\/p>\n<p>Poich\u00e9 abbiamo record di lunghezza variabile, dobbiamo trovare un modo per determinare il posizionamento \/ i confini dei record.<\/p>\n<p><\/p>\n<p>Conosco tre approcci:<\/p>\n<p><\/p>\n<ol>\n<li>Tutti i record sono memorizzati in un flusso continuo, prima c'\u00e8 l'intestazione del record, che contiene la lunghezza, e poi il record stesso.<br \/>\nIn questa opzione sia le intestazioni che i dati possono avere lunghezza variabile.<br \/>\nEssenzialmente, otteniamo una lista collegata, che viene utilizzata ampiamente;<\/li>\n<li>Le intestazioni e i record stessi sono immagazzinati in flussi separati.<br \/>\nUtilizzando intestazioni di lunghezza fissa, otteniamo che la corruzione di un'intestazione non influisce sulle altre.<br \/>\nQuesto approccio \u00e8 utilizzato, ad esempio, in molti file system;<\/li>\n<li>I record sono archiviati in un flusso continuo, il confine del record \u00e8 determinato da un certo marcatore (simbolo \/ sequenza di simboli che sono vietati all'interno dei blocchi di dati). Se all'interno del record appare un marcatore, lo sostituiamo con una certa sequenza (lo mascheriamo).<br \/>\nQuesto approccio \u00e8 utilizzato, per esempio, nel protocollo PPP.<\/li>\n<\/ol>\n<p><\/p>\n<p>Illustro.<\/p>\n<p><\/p>\n<p>Opzione 1:<br \/>\n<img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/e5a9676ca21eaca07e64ddf9fcd9f2eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nQui tutto \u00e8 molto semplice: conoscendo la lunghezza del record possiamo calcolare l'indirizzo della successiva intestazione. In questo modo ci spostiamo tra le intestazioni finch\u00e9 non troviamo un'area riempita di 0xff (area libera) o la fine della pagina.<\/p>\n<p><\/p>\n<p>Opzione 2:<br \/>\n<img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/4fed4860fb659ab6042be9aeeb5c041a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n\u0418\u0437-\u0437\u0430 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u0438 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u0430\u043a \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 (\u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432) \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f. \u041c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u0441\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u043c, \u043d\u043e \u043c\u043d\u0435 \u0441\u0438\u043c\u043f\u0430\u0442\u0438\u0447\u043d\u0435\u0435 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434: \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0440\u0430\u0437\u043c\u0435\u0449\u0430\u0435\u043c \u043d\u0430 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435, \u043e\u0434\u043d\u0430\u043a\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 (\u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430) \u0443 \u043d\u0430\u0441 \u0438\u0434\u0443\u0442 \u043e\u0442 \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0430 \u0434\u0430\u043d\u043d\u044b\u0435 (\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b) \u2014 \u043e\u0442 \u043a\u043e\u043d\u0446\u0430. \u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u043d\u0438 &#171;\u0432\u0441\u0442\u0440\u0435\u0442\u044f\u0442\u0441\u044f&#187; (\u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430 \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u0442 \u043d\u0430 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c) \u2014 \u0441\u0447\u0438\u0442\u0430\u0435\u043c \u044d\u0442\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u043e\u0439.<\/p>\n<p><\/p>\n<p>Opzione 3:<br \/>\n<img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/dfb64009aa7781fcb8a47423ff4160c6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNon \u00e8 necessario memorizzare nell'intestazione la lunghezza o altre informazioni sulla disposizione dei dati; sono sufficienti i marcatori che indicano i confini dei record. Tuttavia, i dati devono essere elaborati durante la scrittura \/ lettura.<br \/>\nCome marcatore utilizzerei 0xff (con cui la pagina \u00e8 riempita dopo la cancellazione), quindi l'area libera non sar\u00e0 mai interpretata come dati.<\/p>\n<p><\/p>\n<p>Tabella comparativa:<\/p>\n<p><\/p>\n<p>Opzione 1<br \/>\nOpzione 2<br \/>\nOpzione 3<\/p>\n<p><strong>Resilienza agli errori<\/strong><br \/>\n\u2014<br \/>\n+<br \/>\n+<\/p>\n<p><strong>Compattezza<\/strong><br \/>\n+<br \/>\n\u2014<br \/>\n+<\/p>\n<p><strong>Complesso di implementazione<\/strong><br \/>\n*<br \/>\n**<br \/>\n**<\/p>\n<p><\/p>\n<p>L'opzione 1 presenta un difetto fatale: se uno dei titoli viene danneggiato, si distrugge l'intera catena successiva. Le altre opzioni permettono di recuperare parte dei dati anche in caso di danni massicci.<br \/>\n\u041d\u043e \u0442\u0443\u0442 \u0443\u043c\u0435\u0441\u0442\u043d\u043e \u0432\u0441\u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0441\u0436\u0430\u0442\u043e\u043c \u0432\u0438\u0434\u0435, \u0442\u0430\u043a \u0438 \u0442\u0430\u043a \u043c\u044b \u0442\u0435\u0440\u044f\u0435\u043c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u043e\u0441\u043b\u0435 &#171;\u0431\u0438\u0442\u043e\u0439&#187; \u0437\u0430\u043f\u0438\u0441\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0445\u043e\u0442\u044c \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0438 \u0441\u0442\u043e\u0438\u0442 \u043c\u0438\u043d\u0443\u0441, \u043c\u044b \u0435\u0433\u043e \u043d\u0435 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c.<\/p>\n<p><\/p>\n<p>Compattezza:<\/p>\n<p><\/p>\n<ul>\n<li>nella prima opzione dobbiamo memorizzare nell'intestazione solo la lunghezza; se usiamo variabili intere di lunghezza variabile, nella maggior parte dei casi possiamo limitarci a un byte;<\/li>\n<li>nella seconda opzione dobbiamo memorizzare l'indirizzo iniziale e la lunghezza; la registrazione deve avere dimensioni fisse, stimo sia di 4 byte per registrazione (due byte per l'offset e due byte per la lunghezza);<\/li>\n<li>per la terza opzione basta un solo carattere per indicare l'inizio della registrazione, pi\u00f9 la registrazione stessa che, a causa dell'escaping, aumenter\u00e0 dell'1-2%. In generale, c'\u00e8 una parit\u00e0 approssimativa con la prima opzione.<\/li>\n<\/ul>\n<p><\/p>\n<p>Inizialmente consideravo la seconda opzione come principale (e avevo anche scritto un'implementazione). Ho abbandonato solo quando ho deciso definitivamente di utilizzare la compressione.<\/p>\n<p><\/p>\n<p><em>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u044f \u0432\u0441\u0451-\u0442\u0430\u043a\u0438 \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043c\u043d\u0435 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0437\u0430\u043d\u0438\u043c\u0430\u0442\u044c\u0441\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u043e\u0440\u0430\u0431\u043b\u044f, \u043a\u0443\u0440\u0441\u0438\u0440\u0443\u044e\u0449\u0435\u0433\u043e \u043c\u0435\u0436\u0434\u0443 \u0417\u0435\u043c\u043b\u0451\u0439 \u0438 \u041c\u0430\u0440\u0441\u043e\u043c \u2014 \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u0440\u0443\u0433\u0438\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u043a \u043d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0441\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0438\u0437\u043b\u0443\u0447\u0435\u043d\u0438\u0435, &#8230;<\/em><\/p>\n<p><\/p>\n<p>Per quanto riguarda la terza opzione: le ho assegnato due stelle per la complessit\u00e0 di implementazione, semplicemente perch\u00e9 non mi piace occuparmi di escaping, modifiche della lunghezza in corso d'opera, ecc. S\u00ec, forse \u00e8 un pregiudizio, ma il codice dovr\u00f2 scriverlo io \u2014 perch\u00e9 costringermi a fare qualcosa che non mi piace.<\/p>\n<p><\/p>\n<p><strong>Riepilogo:<\/strong> \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u0432\u0438\u0434\u0435 \u0446\u0435\u043f\u043e\u0447\u0435\u043a &#171;\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u0434\u043b\u0438\u043d\u043e\u0439 \u2014 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b&#187; \u0438\u0437-\u0437\u0430 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p><\/p>\n<h3 id=\"ispolzovanie-bitovyh-poley-dlya-kontrolya-uspeshnosti-operaciy-zapisi\">Utilizzo di bit per controllare il successo delle operazioni di scrittura<\/h3>\n<p><\/p>\n<p>Non ricordo gi\u00e0 dove ho preso l'idea, ma \u00e8 strutturata all'incirca cos\u00ec:<br \/>\nPer ogni registrazione, riserviamo alcuni bit per memorizzare i flag.<br \/>\n<em>Come abbiamo detto prima, dopo l'operazione erase tutti i bit sono impostati a 1, e possiamo modificare 1 a 0, ma non viceversa.<\/em> \u0422\u0430\u043a \u0447\u0442\u043e \u0434\u043b\u044f &#171;\u0444\u043b\u0430\u0433 \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d&#187; \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c 1, \u0434\u043b\u044f &#171;\u0444\u043b\u0430\u0433 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d&#187; \u2014 0.<\/p>\n<p><\/p>\n<p>Ecco come pu\u00f2 apparire la memorizzazione di dati di lunghezza variabile nella flash:<\/p>\n<p><\/p>\n<ol>\n<li>Impostiamo il flag \"scrittura lunghezza iniziata\";<\/li>\n<li>Scriviamo la lunghezza;<\/li>\n<li>Impostiamo il flag \"scrittura dati iniziata\";<\/li>\n<li>Scriviamo i dati;<\/li>\n<li>Impostiamo il flag \"scrittura completata\".<\/li>\n<\/ol>\n<p><\/p>\n<p>Inoltre, avremo un flag \"si \u00e8 verificato un errore\", quindi 4 flag.<\/p>\n<p><\/p>\n<p>In questo caso abbiamo due stati stabili \"1111\" \u2014 scrittura non iniziata e \"1000\" \u2014 scrittura avvenuta con successo; in caso di interruzioni impreviste del processo di scrittura, otterremo stati intermedi che potremo poi rilevare e gestire.<\/p>\n<p><\/p>\n<p>L'approccio \u00e8 interessante, ma protegge solo da interruzioni improvvise di corrente e eventi simili, il che \u00e8 importante, ma non \u00e8 certo l'unica (e nemmeno la principale) causa di possibili guasti.<\/p>\n<p><\/p>\n<p><strong>Riepilogo:<\/strong> Andiamo avanti nella ricerca di una buona soluzione.<\/p>\n<p><\/p>\n<h3 id=\"kontrolnye-summy\">Checksum<\/h3>\n<p><\/p>\n<p>Le checksum offrono anche la possibilit\u00e0 di assicurarci (con buona probabilit\u00e0) che stiamo leggendo esattamente ci\u00f2 che doveva essere scritto. E, a differenza dei bit di controllo sopra esaminati, funzionano sempre.<\/p>\n<p><\/p>\n<p>Se consideriamo l'elenco delle potenziali fonti di problemi di cui abbiamo parlato, le checksum possono identificare errori indipendentemente dalla loro origine <em>(a meno, forse, che non siano alieni malvagi \u2014 potrebbero falsificare anche le checksum)<\/em>.<\/p>\n<p><\/p>\n<p>Quindi, se il nostro obiettivo \u00e8 verificare che i dati siano integri, le checksum sono un'ottima idea.<\/p>\n<p><\/p>\n<p>La scelta dell'algoritmo per calcolare la checksum non ha sollevato dubbi \u2014 CRC. Da un lato, le propriet\u00e0 matematiche consentono di rilevare al 100% errori di alcuni tipi, dall'altro \u2014 su dati casuali di solito questo algoritmo mostra una probabilit\u00e0 di collisione non molto maggiore rispetto al limite teorico. <img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/bf9cca3564db7d9d03d3ce49642d3a71.jpg\" style=\"display:block;margin: 0 auto;\" \/>. Anche se non \u00e8 l'algoritmo pi\u00f9 veloce, n\u00e9 sempre il minimo in termini di collisioni, ha una qualit\u00e0 molto importante: nei test che ho visto non ho trovato pattern in cui fallisse in modo evidente. La stabilit\u00e0 \u00e8 la qualit\u00e0 principale in questo caso.<\/p>\n<p><\/p>\n<p>Esempio di studio approfondito: <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo.html\">parte 1<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo2.html\">parte 2<\/a><\/noindex> <em>(link a narod.ru, scusate)<\/em>.<\/p>\n<p><\/p>\n<p>Tuttavia, la questione della scelta della checksum non \u00e8 conclusa; il CRC \u00e8 un'intera famiglia di checksum. Dobbiamo decidere la lunghezza e poi scegliere il polinomio.<\/p>\n<p><\/p>\n<p>La scelta della lunghezza della checksum non \u00e8 una questione cos\u00ec semplice come pu\u00f2 sembrare a prima vista.<\/p>\n<p><\/p>\n<p>Illustro:<br \/>\nSupponiamo di avere una probabilit\u00e0 di errore in ogni byte <img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/89a9be2edf2a43cc9115eba37fa02fc8.jpg\" style=\"display:block;margin: 0 auto;\" \/> e un checksum ideale, calcoliamo il numero medio di errori su un milione di record:<\/p>\n<p><\/p>\n<p>Dati, byte<br \/>\nChecksum, byte<br \/>\nErrori non rilevati<br \/>\nFalsi positivi degli errori<br \/>\nTotale degli errori<\/p>\n<p>1<br \/>\n0<br \/>\n1000<br \/>\n0<br \/>\n1000<\/p>\n<p>1<br \/>\n1<br \/>\n4<br \/>\n999<br \/>\n1003<\/p>\n<p>1<br \/>\n2<br \/>\n\u22480<br \/>\n1997<br \/>\n1997<\/p>\n<p>1<br \/>\n4<br \/>\n\u22480<br \/>\n3990<br \/>\n3990<\/p>\n<p>10<br \/>\n0<br \/>\n9955<br \/>\n0<br \/>\n9955<\/p>\n<p>10<br \/>\n1<br \/>\n39<br \/>\n990<br \/>\n1029<\/p>\n<p>10<br \/>\n2<br \/>\n\u22480<br \/>\n1979<br \/>\n1979<\/p>\n<p>10<br \/>\n4<br \/>\n\u22480<br \/>\n3954<br \/>\n3954<\/p>\n<p>1000<br \/>\n0<br \/>\n632305<br \/>\n0<br \/>\n632305<\/p>\n<p>1000<br \/>\n1<br \/>\n2470<br \/>\n368<br \/>\n2838<\/p>\n<p>1000<br \/>\n2<br \/>\n10<br \/>\n735<br \/>\n745<\/p>\n<p>1000<br \/>\n4<br \/>\n\u22480<br \/>\n1469<br \/>\n1469<\/p>\n<p><\/p>\n<p>Sembra tutto semplice: scegli la lunghezza del checksum in base alla lunghezza dei dati protetti, con il minimo di falsi positivi \u2014 e sei a posto.<\/p>\n<p><\/p>\n<p>Tuttavia, con checksum brevi si presenta un problema: sebbene individuino bene gli errori singoli, possono avere una probabilit\u00e0 piuttosto alta di interpretare dati casuali come corretti. Su Habr c'era gi\u00e0 un articolo che descriveva <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/428746\/\">il problema nella vita reale<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Pertanto, per rendere praticamente impossibile una coincidenza casuale del checksum, \u00e8 necessario utilizzare checksum di almeno 32 bit <em>(per lunghezze superiori a 64 bit solitamente si utilizzano funzioni hash crittografiche)<\/em>.<\/p>\n<p><\/p>\n<p>Anche se ho scritto in precedenza che bisogna risparmiare spazio con ogni mezzo, useremo comunque un checksum a 32 bit (16 bit sono pochi, la probabilit\u00e0 di collisione \u00e8 superiore allo 0,01%; mentre 24 bit, come si suol dire, non sono n\u00e9 qui n\u00e9 l\u00ec).<\/p>\n<p><\/p>\n<p>Qui potrebbe sorgere un'obiezione: abbiamo risparmiato ogni byte scegliendo la compressione, quindi adesso dobbiamo dare 4 byte in un colpo solo? Non sarebbe meglio non comprimere e non aggiungere il checksum? Certo, no, l'assenza di compressione <em>non significa<\/em>che non abbiamo bisogno di verificare l'integrit\u00e0.<\/p>\n<p><\/p>\n<p>Per quanto riguarda la scelta del polinomio, non inventiamo la ruota, ma prenderemo il popolare CRC-32C.<br \/>\nQuesto codice rileva 6 errori di bit in pacchetti fino a 22 byte (probabilmente il caso pi\u00f9 comune per noi), 4 errori di bit in pacchetti fino a 655 byte (anche questo \u00e8 un caso frequente per noi), 2 o qualsiasi numero dispari di errori di bit in pacchetti di qualsiasi lunghezza ragionevole.<\/p>\n<p>\n<b class=\"spoiler_title\">Se qualcuno \u00e8 interessato ai dettagli<\/b><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Cyclic_redundancy_check\">Articolo di Wikipedia<\/a><\/noindex> sul CRC.<\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0x8f6e37a0_len.txt\">I parametri del codice crc-32c<\/a><\/noindex> con <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/crc\/notes.html\">sito di Kupman<\/a><\/noindex> \u2014 probabilmente il principale esperto di CRC al mondo.<\/p>\n<p><\/p>\n<p>In <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/networks\/dsn02\/dsn02_koopman.pdf\">il suo articolo<\/a><\/noindex> ci siano <noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0xfa567d89_len.txt\">un altro codice interessante<\/a><\/noindex>, che fornisce parametri leggermente migliori per le lunghezze dei pacchetti a noi rilevanti, ma non ho ritenuto la differenza significativa, e mi sento abbastanza competente da scegliere un codice personalizzato invece di uno standard ben studiato.<\/p>\n<p><\/p>\n<p>Inoltre, dato che i nostri dati sono compressi, sorge la questione: calcolare il checksum sui dati compressi o non compressi?<\/p>\n<p><\/p>\n<p>\u0410\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b &#171;\u0437\u0430&#187; \u043f\u043e\u0434\u0441\u0447\u0451\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b \u043d\u0435\u0441\u0436\u0430\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<p><\/p>\n<ul>\n<li>alla fine dobbiamo verificare l'integrit\u00e0 dei dati memorizzati \u2014 quindi lo verifichiamo direttamente (nel frattempo, verranno controllati eventuali errori nell'implementazione della compressione\/decompressione, danni causati da memoria difettosa, ecc.);<\/li>\n<li>l'algoritmo deflate in zlib ha una realizzazione abbastanza matura e <em>non dovrebbe<\/em> \u043f\u0430\u0434\u0430\u0442\u044c \u043f\u0440\u0438 &#171;\u043a\u0440\u0438\u0432\u044b\u0445&#187; \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u0437\u0430\u0447\u0430\u0441\u0442\u0443\u044e \u043e\u043d \u0441\u043f\u043e\u0441\u043e\u0431\u0435\u043d \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435, \u0441\u043d\u0438\u0437\u0438\u0432 \u043e\u0431\u0449\u0443\u044e \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043d\u0435\u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043a\u0438 (\u043f\u0440\u043e\u0432\u0451\u043b \u0442\u0435\u0441\u0442 \u0441 \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u043e\u0433\u043e \u0431\u0438\u0442\u0430 \u0432 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438, zlib \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0442\u0440\u0435\u0442\u0438 \u0441\u043b\u0443\u0447\u0430\u0435\u0432).<\/li>\n<\/ul>\n<p><\/p>\n<p>\u0410\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b &#171;\u043f\u0440\u043e\u0442\u0438\u0432&#187; \u043f\u043e\u0434\u0441\u0447\u0451\u0442\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u043e\u0439 \u0441\u0443\u043c\u043c\u044b \u043d\u0435\u0441\u0436\u0430\u0442\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<p><\/p>\n<ul>\n<li>CRC &#171;\u0437\u0430\u0442\u043e\u0447\u0435\u043d&#187; \u0438\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u0434 \u043d\u0435\u043c\u043d\u043e\u0433\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435 \u0431\u0438\u0442\u043e\u0432\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b \u0434\u043b\u044f \u0444\u043b\u0435\u0448-\u043f\u0430\u043c\u044f\u0442\u0438 (\u0431\u0438\u0442\u043e\u0432\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430 \u0432 \u0441\u0436\u0430\u0442\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0430\u0442\u044c \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u043e\u043a\u0430, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c, \u0447\u0438\u0441\u0442\u043e \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438, \u043c\u044b \u043c\u043e\u0436\u0435\u043c &#171;\u043f\u043e\u0439\u043c\u0430\u0442\u044c&#187; \u043a\u043e\u043b\u043b\u0438\u0437\u0438\u044e);<\/li>\n<li>non mi piace molto l'idea di passare al decompressore dati che potrebbero essere corrotti, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.cvedetails.com\/vulnerability-list\/vendor_id-72\/product_id-1820\/GNU-Zlib.html\">chi lo sa<\/a><\/noindex>come reagir\u00e0.<\/li>\n<\/ul>\n<p><\/p>\n<p>In questo progetto ho deciso di allontanarmi dalla pratica comune di memorizzare il checksum sui dati non compressi.<\/p>\n<p><\/p>\n<p><strong>Riepilogo:<\/strong> utilizziamo CRC-32C, il checksum viene calcolato sui dati nella forma in cui vengono registrati nella flash (dopo la compressione).<\/p>\n<p><\/p>\n<h3 id=\"izbytochnost\">Ridondanza<\/h3>\n<p><\/p>\n<p>L'uso della codifica ridondante non elimina ovviamente la perdita di dati, tuttavia, pu\u00f2 sostanzialmente (spesso di molti ordini di grandezza) ridurre la probabilit\u00e0 di perdita di dati irreversibile.<\/p>\n<p><\/p>\n<p>Possiamo utilizzare diversi tipi di ridondanza per correggere gli errori.<br \/>\nI codici di Hamming possono correggere errori singoli di bit, i codici di Reed-Solomon sono simbolici, copie multiple di dati insieme a checksum o codificazioni come RAID-6 possono aiutare a ripristinare i dati anche in caso di danni massicci.<br \/>\nInizialmente ero orientato a un ampio utilizzo della codifica a prova di errore, ma poi ho capito che prima bisogna avere un'idea di quali errori vogliamo difenderci, e poi scegliere la codifica.<\/p>\n<p><\/p>\n<p>Abbiamo detto in precedenza che gli errori devono essere rilevati il pi\u00f9 rapidamente possibile. Quando possiamo incontrare errori?<\/p>\n<p><\/p>\n<ol>\n<li>\u041d\u0435\u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c (\u043f\u043e \u043a\u0430\u043a\u0438\u043c-\u043b\u0438\u0431\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u0430\u043f\u0438\u0441\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043b\u043e\u0441\u044c \u043f\u0438\u0442\u0430\u043d\u0438\u0435, \u0437\u0430\u0432\u0438\u0441 Raspberry, &#8230;)<br \/>\nPurtroppo, in caso di tale errore, non rimane altro da fare che ignorare le registrazioni non valide e considerare i dati persi;<\/li>\n<li>Errori di registrazione (per qualche motivo nella memoria flash \u00e8 stato registrato qualcosa di diverso da quanto previsto)<br \/>\nPossiamo rilevare immediatamente tali errori se eseguiamo una lettura di controllo subito dopo la registrazione;<\/li>\n<li>Distorsione dei dati in memoria durante l\u2019archiviazione;<\/li>\n<li>Errori di lettura<br \/>\nPer correggere basta ripetere la lettura pi\u00f9 volte in caso di non corrispondenza del checksum.<\/li>\n<\/ol>\n<p><\/p>\n<p>Cio\u00e8, solo gli errori di terzo tipo (corruzione spontanea dei dati durante l'archiviazione) non possono essere corretti senza codifica resistente agli errori. Si pensa che tali errori siano comunque estremamente improbabili.<\/p>\n<p><\/p>\n<p><strong>Riepilogo:<\/strong> Si \u00e8 deciso di rinunciare alla codifica ridondante, ma se l'operazione dimostrer\u00e0 l'erroneit\u00e0 di questa decisione, si torner\u00e0 a considerare la questione (con gi\u00e0 statistiche accumulate sui guasti, che permetteranno di scegliere il tipo di codifica ottimale).<\/p>\n<p><\/p>\n<h3 id=\"prochee\">Altro<\/h3>\n<p><\/p>\n<p>Ovviamente, il formato dell'articolo non consente di giustificare ogni bit nel formato <em>(e io ho gi\u00e0 esaurito le forze)<\/em>, quindi passer\u00f2 brevemente attraverso alcuni punti non trattati in precedenza.<\/p>\n<p><\/p>\n<ul>\n<li>\u0420\u0435\u0448\u0435\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b &#171;\u0440\u0430\u0432\u043d\u043e\u043f\u0440\u0430\u0432\u043d\u044b\u043c\u0438&#187;<br \/>\nCio\u00e8 non ci saranno pagine speciali con metadati, flussi separati, ecc.; invece ci sar\u00e0 un unico flusso che riscrive tutte le pagine a turno.<br \/>\nQuesto assicura un'usura uniforme delle pagine, l'assenza di un unico punto di guasto, e semplicemente piace;<\/li>\n<li>\u00c8 necessario prevedere la versione del formato.<br \/>\nUn formato senza numero di versione nell'intestazione \u00e8 malvagio!<br \/>\n\u00c8 sufficiente aggiungere nel titolo della pagina un campo con un certo Magic Number (firma), che indicher\u00e0 la versione del formato utilizzato <em>(non penso che nella pratica ce ne siano anche dieci)<\/em>;<\/li>\n<li>Utilizzare per le registrazioni (delle quali ce ne sono molte) un'intestazione di lunghezza variabile, cercando di renderla lunga 1 byte nella maggior parte dei casi;<\/li>\n<li>Per codificare la lunghezza dell'intestazione e la lunghezza della parte troncata della registrazione compressa, utilizzare codici binari di lunghezza variabile.<\/li>\n<\/ul>\n<p><\/p>\n<p>\u00c8 stato molto utile <noindex><a rel=\"nofollow\" href=\"https:\/\/planetcalc.com\/2481\/\">un generatore online<\/a><\/noindex> di codici di Huffman. In pochi minuti sono riuscito a trovare i codici di lunghezza variabile necessari.<\/p>\n<p><\/p>\n<h1 id=\"anchorformatanchoropisanie-formata-hraneniya-dannyh\"><noindex><a rel=\"nofollow\" name=\"format\"><\/a><\/noindex>Descrizione del formato di archiviazione dei dati<\/h1>\n<p><\/p>\n<h2 id=\"byte-order\">Ordinamento dei byte<\/h2>\n<p><\/p>\n<p>I campi di dimensione superiore a un byte sono memorizzati in formato big-endian (ordine dei byte di rete), cio\u00e8 0x1234 \u00e8 registrato come 0x12, 0x34.<\/p>\n<p><\/p>\n<h2 id=\"delenie-na-stranicy\">Divisione in pagine<\/h2>\n<p><\/p>\n<p>L'intera memoria flash \u00e8 suddivisa in pagine di dimensioni uguali.<\/p>\n<p><\/p>\n<p>La dimensione della pagina per impostazione predefinita \u00e8 di 32 Kb, ma non superiore a 1\/4 della dimensione complessiva del chip di memoria (per un chip da 4 Mb si ottengono 128 pagine).<\/p>\n<p><\/p>\n<p>Ogni pagina memorizza i dati in modo indipendente dalle altre (cio\u00e8 i dati di una pagina non fanno riferimento ai dati di un'altra pagina).<\/p>\n<p><\/p>\n<p>Tutte le pagine sono numerate in ordine naturale (in ordine crescente di indirizzi), a partire dal numero 0 (la pagina zero inizia dall'indirizzo 0, la prima dalla 32 Kb, la seconda dalla 64 Kb e cos\u00ec via)<\/p>\n<p><\/p>\n<p>\u041c\u0438\u043a\u0440\u043e\u0441\u0445\u0435\u043c\u0430 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0431\u0443\u0444\u0435\u0440 (ring buffer), \u0442\u043e \u0435\u0441\u0442\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u0438\u0434\u0451\u0442 \u0432 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u043d\u043e\u043c\u0435\u0440\u043e\u043c 0, \u043f\u043e\u0442\u043e\u043c \u0441 \u043d\u043e\u043c\u0435\u0440\u043e\u043c 1, &#8230;, \u043a\u043e\u0433\u0434\u0430 \u043c\u044b \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0442\u043e \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u044b\u0439 \u0446\u0438\u043a\u043b \u0438 \u0437\u0430\u043f\u0438\u0441\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442\u0441\u044f \u0441 \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p><\/p>\n<h2 id=\"vnutri-stranicy\">All'interno della pagina<\/h2>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/39b45f83dc46bb2fd7081ed2a0b638c9.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f 4-\u0431\u0430\u0439\u0442\u043d\u044b\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u043f\u043e\u0442\u043e\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430\u044f \u0441\u0443\u043c\u043c\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 (CRC-32C), \u0434\u0430\u043b\u0435\u0435 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 &#171;\u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a, \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c\u043d\u0430\u044f \u0441\u0443\u043c\u043c\u0430&#187;.<\/p>\n<p><\/p>\n<p>L'intestazione della pagina (nella figura di colore verde sporco) \u00e8 composta da:<\/p>\n<p><\/p>\n<ul>\n<li>un campo di due byte Magic Number (che \u00e8 anche il segnale della versione del formato)<br \/>\nper la versione attuale del formato \u00e8 calcolata come <code>0xed00 \u2295 numero della pagina<\/code>;<\/li>\n<li>\u0434\u0432\u0443\u0445\u0431\u0430\u0439\u0442\u043d\u043e\u0433\u043e \u0441\u0447\u0451\u0442\u0447\u0438\u043a\u0430 &#171;\u0412\u0435\u0440\u0441\u0438\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b&#187; (\u043d\u043e\u043c\u0435\u0440 \u0446\u0438\u043a\u043b\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0438 \u043f\u0430\u043c\u044f\u0442\u0438).<\/li>\n<\/ul>\n<p><\/p>\n<p>Le registrazioni nella pagina sono memorizzate in forma compressa (viene utilizzato l'algoritmo deflate). Tutte le registrazioni su una pagina vengono compresse in un unico flusso (viene utilizzato un dizionario comune), su ogni nuova pagina la compressione inizia da capo. Cio\u00e8, per la decompressione di qualsiasi registrazione sono necessarie tutte le registrazioni precedenti in questa pagina (e solo in questa).<\/p>\n<p><\/p>\n<p>Ogni registrazione \u00e8 compressa con il flag Z_SYNC_FLUSH, e in fondo al flusso compresso risultano 4 byte 0x00, 0x00, 0xff, 0xff, preceduti, forse, da uno o due byte nulli.<br \/>\nQuesta sequenza (di lunghezza 4, 5 o 6 byte) viene scartata durante la scrittura nella memoria flash.<\/p>\n<p><\/p>\n<p>L'intestazione della registrazione consiste in 1, 2 o 3 byte, che memorizzano:<\/p>\n<p><\/p>\n<ul>\n<li>un bit (T), che indica il tipo di registrazione: 0 \u2014 contesto, 1 \u2014 registro;<\/li>\n<li>\u043f\u043e\u043b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0434\u043b\u0438\u043d\u044b (S) \u043e\u0442 1 \u0434\u043e 7 \u0431\u0438\u0442, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0449\u0435\u0435 \u0434\u043b\u0438\u043d\u0443 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 \u0438 &#171;\u0445\u0432\u043e\u0441\u0442&#187;, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u0437\u0430\u043f\u0438\u0441\u0438 \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u0430\u043a\u043e\u0432\u043a\u0438;<\/li>\n<li>la lunghezza della registrazione (L).<\/li>\n<\/ul>\n<p><\/p>\n<p>Tabella dei valori S:<\/p>\n<p><\/p>\n<p>S<br \/>\nLunghezza dell'intestazione, byte<br \/>\nScartato durante la registrazione, byte<\/p>\n<p><code>0<\/code><br \/>\n1<br \/>\n5 (<code>00 00 00 ff ff<\/code>)<\/p>\n<p><code>10<\/code><br \/>\n1<br \/>\n6 (<code>00 00 00 00 ff ff<\/code>)<\/p>\n<p><code>110<\/code><br \/>\n2<br \/>\n4 (<code>00 00 ff ff<\/code>)<\/p>\n<p><code>1110<\/code><br \/>\n2<br \/>\n5 (<code>00 00 00 ff ff<\/code>)<\/p>\n<p><code>11110<\/code><br \/>\n2<br \/>\n6 (<code>00 00 00 00 ff ff<\/code>)<\/p>\n<p><code>1111100<\/code><br \/>\n3<br \/>\n4 (<code>00 00 ff ff<\/code>)<\/p>\n<p><code>1111101<\/code><br \/>\n3<br \/>\n5 (<code>00 00 00 ff ff<\/code>)<\/p>\n<p><code>1111110<\/code><br \/>\n3<br \/>\n6 (<code>00 00 00 00 ff ff<\/code>)<\/p>\n<p><\/p>\n<p>Ho cercato di illustrare, non so quanto sia chiaro:<br \/>\n<img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/8c9b739eec5af4429395c32b4ba4044a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nIn giallo \u00e8 indicato il campo T, in bianco il campo S, in verde L (lunghezza dei dati compressi in byte), in azzurro i dati compressi, in rosso i byte finali dei dati compressi che non vengono scritti nella memoria flash.<\/p>\n<p><\/p>\n<p>In questo modo, potremo registrare gli header di lunghezza pi\u00f9 comune (fino a 63+5 byte in forma compressa) in un solo byte.<\/p>\n<p><\/p>\n<p>Dopo ogni registrazione viene memorizzata una somma di controllo CRC-32C, che utilizza come valore iniziale (init) il valore invertito della somma di controllo precedente.<\/p>\n<p><\/p>\n<p><em>CRC \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e\u043c &#171;\u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438&#187;, \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 (\u043f\u043b\u044e\u0441-\u043c\u0438\u043d\u0443\u0441 \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0438\u0442 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435) \u0442\u0430\u043a\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430: <img decoding=\"async\" alt=\"La mia implementazione di un buffer circolare nella NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/b194d6a3d18ae2f4ae5d4054a93c0ea3.jpg\" style=\"display:block;margin: 0 auto;\" \/>.<br \/>\nIn altre parole, calcoliamo il CRC di tutti i byte precedenti degli header e dei dati in questa pagina.<\/em><\/p>\n<p><\/p>\n<p>Subito dopo la somma di controllo si trova l'header della registrazione successiva.<\/p>\n<p><\/p>\n<p>L'header \u00e8 costruito in modo tale che il suo primo byte sia sempre diverso da 0x00 e 0xff (se incontriamo 0xff al posto del primo byte dell'header, significa che \u00e8 un'area non utilizzata; 0x00 segnala un errore).<\/p>\n<p><\/p>\n<h2 id=\"primernye-algoritmy\">Algoritmi approssimativi<\/h2>\n<p><\/p>\n<h3 id=\"chtenie-iz-flesh-pamyati\">Lettura dalla memoria flash<\/h3>\n<p><\/p>\n<p>Qualsiasi lettura avviene con verifica della somma di controllo.<br \/>\nSe la somma di controllo non corrisponde, la lettura viene ripetuta pi\u00f9 volte nella speranza di leggere i dati corretti.<\/p>\n<p><\/p>\n<p><em>(ha senso, Linux non memorizza nella cache le letture dalla NOR Flash, confermato)<\/em><\/p>\n<p><\/p>\n<h3 id=\"zapis-v-flesh-pamyat\">Scrittura nella memoria flash<\/h3>\n<p><\/p>\n<p>Registriamo i dati.<br \/>\nLi leggiamo.<\/p>\n<p><\/p>\n<p>Se i dati letti non corrispondono a quelli scritti, riempiamo l'area di zeri e segnaliamo un errore.<\/p>\n<p><\/p>\n<h3 id=\"podgotovka-novoy-mikroshemy-k-rabote\">Preparazione del nuovo chip per il lavoro<\/h3>\n<p><\/p>\n<p>Per l'inizializzazione, nella prima (o meglio, zero) pagina si scrive un header con versione 1.<br \/>\nDopo di che, in questa pagina viene scritto il contesto iniziale (contiene UUID della macchina e impostazioni predefinite). <\/p>\n<p><\/p>\n<p>Ecco, la memoria flash \u00e8 pronta per l'uso.<\/p>\n<p><\/p>\n<h3 id=\"zagruzka-avtomata\">Avvio della macchina<\/h3>\n<p><\/p>\n<p>Durante l'avvio si leggono i primi 8 byte di ogni pagina (header + CRC), le pagine con un Magic Number sconosciuto o un CRC errato vengono ignorate.<br \/>\n\u0418\u0437 &#171;\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0445&#187; \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0432\u044b\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0441 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439, \u0438\u0437 \u043d\u0438\u0445 \u0431\u0435\u0440\u0451\u0442\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430, \u0438\u043c\u0435\u044e\u0449\u0430\u044f \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0438\u0439 \u043d\u043e\u043c\u0435\u0440.<br \/>\n\u0421\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c CRC, \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0444\u043b\u0430\u0433\u0430 &#171;\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442&#187;. \u0415\u0441\u043b\u0438 \u0432\u0441\u0451 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u2014 \u044d\u0442\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439. \u0415\u0441\u043b\u0438 \u043d\u0435\u0442 \u2014 \u043e\u0442\u043a\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u0441\u044f \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0451\u043c &#171;\u0436\u0438\u0432\u0443\u044e&#187; \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443.<br \/>\n\u0430 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u0437\u0430\u043f\u0438\u0441\u0438, \u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441 \u0444\u043b\u0430\u0433\u043e\u043c &#171;\u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442&#187; \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c.<br \/>\nSalviamo il dizionario zlib (ne avremo bisogno per ulteriori registrazioni in questa pagina).<\/p>\n<p><\/p>\n<p>Tutto, il caricamento \u00e8 completato, il contesto \u00e8 ripristinato, possiamo lavorare.<\/p>\n<p><\/p>\n<h3 id=\"dobavlenie-zapisi-v-zhurnal\">Aggiunta di una registrazione al registro<\/h3>\n<p><\/p>\n<p>Comprimi la registrazione con il dizionario corretto, specificando Z_SYNC_FLUSH. Controlliamo se la registrazione compressa entra nella pagina attuale.<br \/>\nSe non ci sta (o se ci sono errori CRC nella pagina), iniziamo una nuova pagina (vedi sotto).<br \/>\nRegistriamo la registrazione e il CRC. Se si verifica un errore, iniziamo una nuova pagina.<\/p>\n<p><\/p>\n<h3 id=\"novaya-stranica\">Nuova pagina<\/h3>\n<p><\/p>\n<p>Selezioniamo una pagina libera con il numero pi\u00f9 basso (consideriamo libera una pagina con somma di controllo errata nell'header o con versione inferiore a quella attuale). Se non ci sono pagine di questo tipo, scegliamo la pagina con il numero pi\u00f9 basso tra quelle con versione uguale a quella attuale.<br \/>\nEseguiamo l'erase della pagina scelta. Verifichiamo il contenuto con 0xff. Se qualcosa non va, prendiamo la successiva pagina libera, e cos\u00ec via.<br \/>\nNella pagina cancellata scriviamo l'header, come prima registrazione lo stato attuale del contesto, la successiva \u2014 la registrazione non scritta del registro (se c'\u00e8).<\/p>\n<p><\/p>\n<h1 id=\"primenimost-formata\">Applicabilit\u00e0 del formato<\/h1>\n<p><\/p>\n<p>A mio avviso, \u00e8 emerso un buon formato per memorizzare qualsiasi flusso di informazioni semi-comprimibili (testo semplice, JSON, MessagePack, CBOR, possibilmente protobuf) nella NOR Flash.<\/p>\n<p><\/p>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0444\u043e\u0440\u043c\u0430\u0442 &#171;\u0437\u0430\u0442\u043e\u0447\u0435\u043d&#187; \u043f\u043e\u0434 SLC NOR Flash.<\/p>\n<p><\/p>\n<p>Non dovrebbe essere usato con supporti ad alto BER, come NAND o MLC NOR <em>(esiste davvero una memoria di questo tipo in commercio? Ho visto solo riferimenti nei lavori sui codici di correzione)<\/em>.<\/p>\n<p><\/p>\n<p>Ancora di pi\u00f9, non dovrebbe essere usato con dispositivi che hanno il proprio FTL: USB flash, SD, MicroSD, ecc. <em>(\u0434\u043b\u044f \u0442\u0430\u043a\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438 \u044f \u0434\u0435\u043b\u0430\u043b \u0444\u043e\u0440\u043c\u0430\u0442 \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0432 512 \u0431\u0430\u0439\u0442, \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u043e\u0439 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0438 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043d\u043e\u043c\u0435\u0440\u0430\u043c\u0438 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u2014 \u0438\u043d\u043e\u0433\u0434\u0430 \u0438\u0437 &#171;\u0433\u043b\u044e\u043a\u043d\u0443\u0432\u0448\u0435\u0439&#187; \u0444\u043b\u0435\u0448\u043a\u0438 \u0443\u0434\u0430\u0432\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0447\u0442\u0435\u043d\u0438\u0435\u043c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435)<\/em>.<\/p>\n<p><\/p>\n<p>A seconda delle esigenze, il formato pu\u00f2 essere utilizzato senza modifiche su chiavette USB da 128 Kbit (16 Kb) fino a 1 Gbit (128 Mb). Se desiderato, pu\u00f2 essere utilizzato anche su chip di maggiore capacit\u00e0, solo che probabilmente \u00e8 necessario regolare la dimensione della pagina <em>(Ma qui si pone gi\u00e0 il problema della convenienza economica, il prezzo della NOR Flash di grande capacit\u00e0 non \u00e8 incoraggiante)<\/em>.<\/p>\n<p><\/p>\n<p>Se a qualcuno il formato \u00e8 sembrato interessante e vuole utilizzarlo in un progetto aperto, scrivete pure, cercher\u00f2 di trovare il tempo per sistemare il codice e caricarlo su github.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Conclusione<\/h1>\n<p><\/p>\n<p>Come vediamo, alla fine il formato si \u00e8 rivelato semplice <em>e persino noioso<\/em>.<\/p>\n<p><\/p>\n<p>Nell'articolo \u00e8 difficile riflettere l'evoluzione del mio punto di vista, ma credetemi: inizialmente volevo creare qualcosa di complesso, indistruttibile, capace di sopravvivere anche a un'esplosione nucleare nei dintorni. Tuttavia, la ragione (spero) ha prevalso e gradualmente le priorit\u00e0 si sono spostate verso la semplicit\u00e0 e la compattezza.<\/p>\n<p><\/p>\n<p>\u00c8 possibile che mi sia sbagliato? S\u00ec, certo. Potrebbe benissimo capitare, ad esempio, che abbiamo acquistato un lotto di chip scadenti. Oppure per qualche altro motivo l'attrezzatura non soddisfer\u00e0 le aspettative in termini di affidabilit\u00e0.<\/p>\n<p><\/p>\n<p>Ho un piano per questo caso? Credo che leggendo l'articolo non dubitiate che ci sia un piano. E non uno solo.<\/p>\n<p><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u043e, \u0444\u043e\u0440\u043c\u0430\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0438 \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0438 \u043a\u0430\u043a &#171;\u043f\u0440\u043e\u0431\u043d\u044b\u0439 \u0448\u0430\u0440&#187;.<\/p>\n<p><\/p>\n<p>Ad oggi tutto funziona normalmente, letteralmente nei prossimi giorni la soluzione sar\u00e0 implementata <em>(circa)<\/em> \u043d\u0430 \u0441\u043e\u0442\u043d\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432 &#171;\u0431\u043e\u0435\u0432\u043e\u0439&#187; \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438 (\u0431\u043b\u0430\u0433\u043e, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0444\u043e\u0440\u043c\u0430\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u0434\u0451\u0436\u043d\u043e \u0434\u0435\u0442\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0431\u043e\u0438; \u0442\u0430\u043a \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443). \u0427\u0435\u0440\u0435\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0441\u044f\u0446\u0435\u0432 \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u044b\u0432\u043e\u0434\u044b <em>(e se non va bene, anche prima)<\/em>.<\/p>\n<p><\/p>\n<p>Se al termine dell'uso si rivelano problemi seri e sono necessarie modifiche, ne dar\u00f2 sicuramente notizia.<\/p>\n<p><\/p>\n<h1 id=\"literatura\">Letteratura<\/h1>\n<p><\/p>\n<p>Non volevo creare un lungo elenco noioso di lavori utilizzati, alla fine Google ce l'ha tutti.<\/p>\n<p><\/p>\n<p>Qui ho deciso di lasciare un elenco delle scoperte che mi sono sembrate particolarmente interessanti, ma gradualmente sono state integrate direttamente nel testo dell'articolo, e nell'elenco \u00e8 rimasto un solo punto:<\/p>\n<p><\/p>\n<ol>\n<li>Utility <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/madler\/infgen\/\">infgen<\/a><\/noindex> di autore zlib. \u00c8 in grado di visualizzare in modo chiaro il contenuto di archivi deflate\/zlib\/gzip. Se vi capita di dover capire il funzionamento interno del formato deflate (o gzip), lo consiglio vivamente.<\/li>\n<\/ol>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/479044\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u043c\u043e\u043d\u0435\u0442\u043e\u043f\u0440\u0438\u0451\u043c\u043d\u0438\u043a, \u043a\u0443\u043f\u044e\u0440\u043e\u043f\u0440\u0438\u0451\u043c\u043d\u0438\u043a, \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0439 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u2026 \u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430. \u0412\u0441\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0432 \u0436\u0443\u0440\u043d\u0430\u043b \u043d\u0430 \u0444\u043b\u0435\u0448\u043a\u0435 (MicroSD), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 (\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e USB-\u043c\u043e\u0434\u0435\u043c\u0430) \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0442\u0430\u043c \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0411\u0414. \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u0440\u043e\u0434\u0430\u0436\u0430\u0445 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u0432 1\u0441, \u0442\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-53751","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u043b\u044c\u0446\u0435\u0432\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430 \u0432 NOR flash | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-12-08T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:01:41+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47La mia implementazione di un buffer circolare in NOR flash | ProHoster","description":"Storia Ci sono distributori automatici di nostra progettazione. All'interno ci sono Raspberry Pi e un po' di cablaggio su una scheda separata.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u043b\u044c\u0446\u0435\u0432\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430 \u0432 NOR flash | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-12-08T21:00:00+00:00","article:modified_time":"2020-02-18T11:01:41+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"53751","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-24 08:36:23","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:18:30","updated":"2026-01-24 08:36:23","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/53751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=53751"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/53751\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=53751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=53751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=53751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}