{"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\/de\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","title":{"rendered":"Meine Implementierung eines Ringspeichers im NOR-Flash","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h1 id=\"predystoriya\">Hintergrund<\/h1>\n<p><\/p>\n<p>Wir haben eigens entwickelte Verkaufsautomaten. Im Inneren befindet sich ein Raspberry Pi sowie eine kleine Zusatzplatine. Angeschlossen sind ein M\u00fcnzpr\u00fcfer, ein Geldscheineinzug und ein Bankterminal... Alles wird von einer selbstgeschriebenen Software gesteuert. S\u00e4mtliche Betriebsdaten werden in einem Protokoll auf einem Flash-Speicher (MicroSD) gespeichert, das dann \u00fcber das Internet (mithilfe eines USB-Modems) an einen Server \u00fcbertragen wird, wo sie in einer Datenbank abgelegt werden. Verkaufsinformationen werden in 1C hochgeladen, zudem gibt es ein einfaches Web-Interface zur \u00dcberwachung usw. <\/p>\n<p><\/p>\n<p>Das Protokoll ist also von entscheidender Bedeutung \u2013 f\u00fcr die Buchf\u00fchrung (Einnahmen, Verk\u00e4ufe usw.), die \u00dcberwachung (verschiedene St\u00f6rungen und andere unvorhergesehene Ereignisse); man kann sagen, dies sind alle Informationen, die wir \u00fcber diesen Automaten haben. <\/p>\n<p><\/p>\n<h1 id=\"problema\">Das Problem<\/h1>\n<p><\/p>\n<p>USB-Sticks erweisen sich als sehr unzuverl\u00e4ssige Ger\u00e4te. Sie gehen mit bemerkenswerter Regelm\u00e4\u00dfigkeit kaputt. Dies f\u00fchrt sowohl zu Ausf\u00e4llen der Automaten als auch (wenn aus bestimmten Gr\u00fcnden das Protokoll nicht online \u00fcbertragen werden kann) zu Datenverlusten.<\/p>\n<p><\/p>\n<p><em>Dies ist nicht das erste Mal, dass ich USB-Sticks verwende. Zuvor hatte ich ein anderes Projekt mit \u00fcber hundert Ger\u00e4ten, bei dem das Protokoll auf USB-Sticks gespeichert wurde. Auch dort gab es Probleme mit der Zuverl\u00e4ssigkeit; manchmal z\u00e4hlte die Anzahl der defekten Ger\u00e4te im Monat in die Dutzende. Wir haben verschiedene Sticks ausprobiert, einschlie\u00dflich Markenmodelle mit SLC-Speicher. Einige Modelle waren zuverl\u00e4ssiger als andere, aber der Austausch der Sticks hat das Problem nicht grundlegend gel\u00f6st.<\/em><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <\/p>\n<p><strong>Achtung!<\/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\">Ende<\/a><\/noindex> des Artikels gehen.<\/p>\n<p><\/p>\n<h1 id=\"reshenie\">L\u00f6sung<\/h1>\n<p><\/p>\n<p>Das Erste, was mir in den Sinn kommt: auf MicroSD zu verzichten, stattdessen beispielsweise SSDs zu verwenden und von diesen zu booten. Theoretisch m\u00f6glich, denke ich, aber relativ teuer und nicht so zuverl\u00e4ssig (ein USB-SATA-Adapter kommt hinzu; auch bei preiswerten SSDs ist die Ausfallrate nicht erfreulich).<\/p>\n<p><\/p>\n<p>USB-Festplatten scheinen ebenfalls keine besonders attraktive L\u00f6sung zu sein.<\/p>\n<p><\/p>\n<p>Deshalb sind wir zu folgender Option gekommen: Den Bootvorgang von der MicroSD beizubehalten, aber sie im read-only Modus zu verwenden und das Arbeitsprotokoll (sowie andere, f\u00fcr das jeweilige Ger\u00e4t einzigartige Informationen \u2013 Seriennummer, Kalibrierungen der Sensoren usw.) an einem anderen Ort zu speichern. <\/p>\n<p><\/p>\n<p>Das Thema des schreibgesch\u00fctzten Dateisystems f\u00fcr die Raspberry Pi wurde bereits ausf\u00fchrlich behandelt, daher m\u00f6chte ich in diesem Artikel nicht auf die Details der Implementierung eingehen. <em>(aber falls Interesse besteht, k\u00f6nnte ich vielleicht einen kleinen Artikel zu diesem Thema schreiben.)<\/em>Ein einziger Punkt ist erw\u00e4hnenswert: Sowohl aus pers\u00f6nlicher Erfahrung als auch aus R\u00fcckmeldungen von Nutzern, die bereits implementiert haben, gibt es einen Gewinn in der Zuverl\u00e4ssigkeit. Ja, es ist unm\u00f6glich, alle Ausf\u00e4lle zu vermeiden, aber die Frequenz erheblich zu reduzieren \u2013 das ist durchaus realistisch. Au\u00dferdem werden die Karten einheitlich, was den Austausch f\u00fcr das Wartungspersonal deutlich vereinfacht.<\/p>\n<p><\/p>\n<h2 id=\"apparatnaya-chast\">Hardware<\/h2>\n<p><\/p>\n<p>Bei der Auswahl des Speichertyps gab es keine besonderen Zweifel \u2013 NOR Flash.<br \/>\nArgumente: <\/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>l\u00e4cherlicher Preis;<\/li>\n<li>standardisiertes Protokoll (Implementierungen sind bereits im Linux-Kernel vorhanden, man kann auch externe L\u00f6sungen verwenden oder sogar eigene schreiben, da alles recht einfach ist);<\/li>\n<li>Zuverl\u00e4ssigkeit und Lebensdauer:<br \/>\naus einem typischen Datenblatt: Die Daten werden 20 Jahre lang gespeichert, 100.000 L\u00f6schzyklen f\u00fcr jeden Block;<br \/>\naus externen Quellen: extrem niedriger BER, es wird die Notwendigkeit von Fehlerkorrekturcodes postuliert <em>(in einigen Arbeiten wird ECC f\u00fcr NOR behandelt, meistens ist jedoch MLC NOR gemeint, es kommt auch vor)<\/em>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Lassen Sie uns die Anforderungen an Volumen und Ressourcen absch\u00e4tzen.<\/p>\n<p><\/p>\n<p>Es ist wichtig, dass die Daten \u00fcber mehrere Tage hinweg garantiert gespeichert werden. Dies ist notwendig, um sicherzustellen, dass im Falle von Kommunikationsproblemen die Verkaufsdaten nicht verloren gehen. Wir orientieren uns an 5 Tagen, in diesem Zeitraum <em>(selbst unter Ber\u00fccksichtigung von Wochenenden und Feiertagen)<\/em> kann das Problem gel\u00f6st werden.<\/p>\n<p><\/p>\n<p>Derzeit sammeln wir t\u00e4glich etwa 100 kB Log-Daten (3-4 Tausend Eintr\u00e4ge), jedoch w\u00e4chst diese Zahl allm\u00e4hlich \u2014 die Detailgenauigkeit nimmt zu, neue Ereignisse werden hinzugef\u00fcgt. Au\u00dferdem gibt es manchmal Ausrei\u00dfer (ein Sensor f\u00e4ngt an, falsche Alarme zu spammen, zum Beispiel). Wir rechnen mit 10.000 Eintr\u00e4gen zu 100 Byte \u2014 das sind ein Megabyte pro Tag.<\/p>\n<p><\/p>\n<p>Insgesamt kommen wir auf 5 MB an komprimierbaren (guten) Daten. Dazu kommen noch <em>(grob gesch\u00e4tzt)<\/em> 1 MB an Steuerdaten.<\/p>\n<p><\/p>\n<p>Das hei\u00dft, wir ben\u00f6tigen einen Chip mit 8 MB, wenn wir keine Komprimierung verwenden, oder 4 MB, wenn wir sie verwenden. V\u00f6llig realistische Zahlen f\u00fcr diesen Speichertyp.<\/p>\n<p><\/p>\n<p>Betrachtet man die Ressource: Wenn wir annehmen, dass der Speicher nicht h\u00e4ufiger als alle 5 Tage neu beschrieben wird, ergeben sich \u00fcber 10 Jahre weniger als tausend Schreibzyklen.<br \/>\nIch erinnere daran, dass der Hersteller hunderttausend verspricht.<\/p>\n<p>\n<b class=\"spoiler_title\">Ein wenig \u00fcber NOR vs NAND<\/b><\/p>\n<p>Heutzutage ist NAND-Speicher nat\u00fcrlich deutlich beliebter, aber f\u00fcr dieses Projekt w\u00fcrde ich ihn nicht verwenden: NAND erfordert im Gegensatz zu NOR unbedingt die Verwendung von Fehlerkorrekturcodes, Defektlisten usw., und die Pins von NAND-Chips sind normalerweise auch deutlich zahlreicher.<\/p>\n<p><\/p>\n<p>Als Nachteile von NOR k\u00f6nnen aufgef\u00fchrt werden:<\/p>\n<p><\/p>\n<ul>\n<li>geringer Speicher (und damit ein hoher Preis pro Megabyte);<\/li>\n<li>nicht sehr hohe \u00dcbertragungsgeschwindigkeit (haupts\u00e4chlich wegen der Verwendung eines seriellen Interfaces, normalerweise SPI oder I2C);<\/li>\n<li>langsame L\u00f6schung (je nach Blockgr\u00f6\u00dfe dauert sie von wenigen Millisekunden bis zu mehreren Sekunden).<\/li>\n<\/ul>\n<p><\/p>\n<p>Scheinbar nichts Kritisches f\u00fcr uns, also machen wir weiter.<\/p>\n<p><\/p>\n<p>Falls weitere Details interessieren, wurde der Chip ausgew\u00e4hlt <noindex><a rel=\"nofollow\" href=\"https:\/\/www.adestotech.com\/wp-content\/uploads\/doc3686.pdf\">at25df321a<\/a><\/noindex> <em>(aber das ist unwesentlich, der Markt ist voll von Alternativen, die mit der Pinbelegung und dem Befehlssystem kompatibel sind; selbst wenn wir einen Chip eines anderen Herstellers und\/oder in einem anderen Umfang installieren wollen, funktioniert alles ohne \u00c4nderung des Codes)<\/em>.<\/p>\n<p><\/p>\n<p>Ich verwende den im Linux-Kernel eingebetteten Treiber; auf Raspberry ist es dank der Unterst\u00fctzung von Device Tree Overlays ganz einfach \u2013 man muss einfach das kompilierte Overlay in \/boot\/overlays legen und ein wenig \/boot\/config.txt modifizieren.<\/p>\n<p>\n<b class=\"spoiler_title\">Beispiel f\u00fcr eine dts-Datei<\/b><\/p>\n<p>Ehrlich gesagt bin ich mir nicht sicher, ob es ohne Fehler geschrieben ist, aber es funktioniert.<\/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\">Und noch eine Zeile in config.txt<\/b><\/p>\n<pre><code class=\"plaintext\">dtoverlay=at25:spimaxfrequency=50000000<\/code><\/pre>\n<p><\/p>\n<p>Die Beschreibung der Verbindung des Chips mit dem Raspberry Pi lasse ich weg. Einerseits bin ich kein Elektronikexperte, andererseits ist es sogar f\u00fcr mich banaler: Der Chip hat nur 8 Pins, von denen wir Erde, Strom, SPI (CS, SI, SO, SCK) ben\u00f6tigen; die Pegel stimmen mit denen des Raspberry Pi \u00fcberein, zus\u00e4tzliche Verkabelung ist nicht erforderlich \u2013 einfach die angegebenen 6 Kontakte verbinden.<\/p>\n<p><\/p>\n<h2 id=\"postanovka-zadachi\">Problemstellung<\/h2>\n<p><\/p>\n<p>Wie \u00fcblich erfolgt die Aufgabenstellung in mehreren Iterationen; ich glaube, jetzt ist es an der Zeit f\u00fcr die n\u00e4chste. Also lasst uns innehalten, alles zusammenfassen, was bereits geschrieben wurde, und die verbliebenen Details kl\u00e4ren.<\/p>\n<p><\/p>\n<p>Wir haben uns entschieden, dass das Protokoll im SPI NOR Flash gespeichert wird.<\/p>\n<p>\n<b class=\"spoiler_title\">Was ist NOR Flash f\u00fcr diejenigen, die es nicht wissen?<\/b><\/p>\n<p>Es handelt sich um nichtfl\u00fcchtigen Speicher, mit dem drei Operationen durchgef\u00fchrt werden k\u00f6nnen:<\/p>\n<p><\/p>\n<ol>\n<li>Lesen:<br \/>\nDas ganz gew\u00f6hnliche Lesen: Wir geben die Adresse an und lesen so viele Bytes, wie wir ben\u00f6tigen;<\/li>\n<li>Schreiben:<br \/>\nDas Schreiben in NOR Flash sieht zwar gew\u00f6hnlich aus, hat aber eine Besonderheit: Man kann nur 1 in 0 \u00e4ndern, aber nicht umgekehrt. Wenn in unserer Speicherzelle z.B. 0x55 stand, wird nach dem Schreiben von 0x0f dort 0x05 gespeichert. <em>(siehe Tabelle etwas weiter unten)<\/em>;<\/li>\n<li>L\u00f6schen:<br \/>\nNat\u00fcrlich m\u00fcssen wir auch die umgekehrte Operation durchf\u00fchren k\u00f6nnen \u2013 0 in 1 \u00e4ndern, daf\u00fcr gibt es die L\u00f6schoperation. Im Gegensatz zu den ersten beiden operiert sie nicht mit Bytes, sondern mit Bl\u00f6cken (der minimale L\u00f6schblock im gew\u00e4hlten Chip betr\u00e4gt 4 KB). Das L\u00f6schen vernichtet den gesamten Block und ist der einzige Weg, 0 in 1 zu \u00e4ndern. Daher muss man beim Arbeiten mit Flash-Speicher h\u00e4ufig die Datenstrukturen an die Grenze des L\u00f6schblocks anpassen.<br \/>\nSchreiben in NOR Flash:<\/li>\n<\/ol>\n<p><\/p>\n<p>Bin\u00e4re Daten<\/p>\n<p><strong>War<\/strong><br \/>\n<code>01010101<\/code><\/p>\n<p><strong>Geschrieben<\/strong><br \/>\n<code>00001111<\/code><\/p>\n<p><strong>Ist<\/strong><br \/>\n<code>00000101<\/code><\/p>\n<p><\/p>\n<p>Das Journal stellt eine Sequenz von variablen L\u00e4nge dar. Die typische L\u00e4nge eines Eintrags betr\u00e4gt etwa 30 Bytes (obwohl es manchmal auch Eintr\u00e4ge mit mehreren Kilobytes gibt). <em>In diesem Fall arbeiten wir mit ihnen einfach als mit einem Byte-Array, aber, falls es interessiert, wird innerhalb der Eintr\u00e4ge CBOR verwendet.<\/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 \/>\nDiese Informationen stellen eine Reihe von Schl\u00fcssel-Wert-Paaren dar, die ebenfalls in CBOR gespeichert sind. Diese Informationen sind nicht sehr umfangreich (h\u00f6chstens einige Kilobytes) und werden nicht h\u00e4ufig aktualisiert.<br \/>\nIm Folgenden werden wir sie Kontext nennen.<\/p>\n<p><\/p>\n<p>Wenn wir uns erinnern, womit dieser Artikel begann, ist es sehr wichtig, die Zuverl\u00e4ssigkeit der Datenspeicherung zu gew\u00e4hrleisten und, wenn m\u00f6glich, einen ununterbrochenen Betrieb selbst im Falle von Hardwarefehlern\/Datenbesch\u00e4digungen sicherzustellen.<\/p>\n<p><\/p>\n<p>Welche Problemquellen k\u00f6nnen wir betrachten?<\/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 \/>\nInformationen aus <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\">Diskussion<\/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>Fehler im Flash-Speicher selbst.<br \/>\nBER ist zwar sehr niedrig, kann aber nicht null sein;<\/li>\n<li>Busfehler<br \/>\nDie \u00fcber SPI \u00fcbermittelten Daten sind nicht gesch\u00fctzt, sodass sowohl einzelne Bitfehler als auch Synchronisationsfehler \u2013 das Verlieren oder Einf\u00fcgen von Bits (was zu massiven Datenverzerrungen f\u00fchrt) \u2013 auftreten k\u00f6nnen;<\/li>\n<li>Andere Fehler\/St\u00f6rungen<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>Ich habe Anforderungen formuliert, deren Erf\u00fcllung meiner Meinung nach notwendig ist, um die Zuverl\u00e4ssigkeit sicherzustellen:<\/p>\n<p><\/p>\n<ul>\n<li>Schreibvorg\u00e4nge m\u00fcssen sofort in den Flash-Speicher gelangen, verz\u00f6gerte Schreibvorg\u00e4nge werden nicht ber\u00fccksichtigt; - wenn ein Fehler auftritt, sollte er so fr\u00fch wie m\u00f6glich erkannt und behandelt werden; - das System sollte nach M\u00f6glichkeit nach Fehlern wieder funktionsf\u00e4hig gemacht werden.<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\">Ideen, Ans\u00e4tze, \u00dcberlegungen<\/h2>\n<p><\/p>\n<p>Als ich begann, \u00fcber dieses Problem nachzudenken, schossen mir viele Ideen durch den Kopf, zum Beispiel:<\/p>\n<p><\/p>\n<ul>\n<li>Datenkompression verwenden;<\/li>\n<li>schlaue Datenstrukturen nutzen, zum Beispiel die Header separat von den Datens\u00e4tzen zu speichern, damit man im Falle eines Fehlers in einem Datensatz die anderen problemlos lesen kann;<\/li>\n<li>Bitfelder zur Kontrolle der Vollst\u00e4ndigkeit der Daten bei Stromausfall verwenden;<\/li>\n<li>Pr\u00fcfziffern f\u00fcr alles speichern;<\/li>\n<li>irgendeine Art von fehlerresistenter Codierung einsetzen.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ein Teil dieser Ideen wurde umgesetzt, andere wurden verworfen. Lassen Sie uns der Reihe nach vorgehen.<\/p>\n<p><\/p>\n<h3 id=\"szhatie-dannyh\">Datenkompression<\/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>Die Overheadkosten f\u00fcr die Kompression sind unbedeutend (unser Prozessor ist leistungsstark genug; selbst im ersten Pi hatte es einen Kern mit 700 MHz, w\u00e4hrend aktuelle Modelle mehrere Kerne mit \u00fcber einem Gigahertz besitzen). Die Speicher\u00fcbertragungsrate ist nicht hoch (einige Megabyte pro Sekunde) und die Datensatzgr\u00f6\u00dfen sind gering. Insgesamt, auch wenn die Kompression die Leistung beeinflussen sollte, wird es nur einen positiven Effekt haben. <em>(v\u00f6llig unwichtig, nur eine Feststellung)<\/em>. Zudem verwenden wir kein echtes Embedded-System, sondern ein regul\u00e4res Linux \u2014 daher sollte die Implementierung nicht viel Aufwand erfordern (es gen\u00fcgt, die Bibliothek anzubinden und einige Funktionen daraus zu verwenden).<\/p>\n<p><\/p>\n<p>Ein Ausschnitt aus einem Log eines laufenden Ger\u00e4ts (1,7 MB, 70.000 Eintr\u00e4ge) wurde genommen und zun\u00e4chst auf Komprimierbarkeit mit den auf dem Computer verf\u00fcgbaren Werkzeugen gzip, lz4, lzop, bzip2, xz, zstd getestet.<\/p>\n<p><\/p>\n<ul>\n<li>gzip, xz, zstd zeigten \u00e4hnliche Ergebnisse (40 KB).<br \/>\nEs war \u00fcberraschend, dass das angesagte xz hier auf einem Niveau mit gzip oder zstd abschnitt;<\/li>\n<li>lzip mit den Standardeinstellungen lieferte ein etwas schlechteres Ergebnis;<\/li>\n<li>lz4 und lzop zeigten ein eher unbefriedigendes Ergebnis (150 KB);<\/li>\n<li>bzip2 zeigte \u00fcberraschend gute Ergebnisse (18 KB).<\/li>\n<\/ul>\n<p><\/p>\n<p>Zusammenfassend l\u00e4sst sich sagen, dass die Daten sehr gut komprimierbar sind.<br \/>\nAlso, (sofern wir keine fatalen M\u00e4ngel finden) wird es also eine Kompression geben! Einfach weil mehr Daten auf dasselbe Flash-Laufwerk passen.<\/p>\n<p><\/p>\n<p>Lassen Sie uns \u00fcber die Nachteile nachdenken.<\/p>\n<p><\/p>\n<p>Das erste Problem: Wir haben bereits vereinbart, dass jeder Datensatz sofort auf das Flash-Speicherger\u00e4t \u00fcbertragen werden muss. Normalerweise sammelt ein Archivierungsprogramm Daten aus dem Eingangsstrom, bis es entscheidet, dass es Zeit ist, in den Ausgang zu schreiben. Wir hingegen ben\u00f6tigen sofort einen komprimierten Datenblock und m\u00fcssen ihn im nichtfl\u00fcchtigen Speicher ablegen.<\/p>\n<p><\/p>\n<p>Ich sehe drei M\u00f6glichkeiten:<\/p>\n<p><\/p>\n<ol>\n<li>Jeden Datensatz mit Hilfe von W\u00f6rterbuchkompression zu komprimieren, anstatt die oben besprochenen Algorithmen zu verwenden.<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 \/>\nDie in Betracht gezogenen Kompressionsalgorithmen sind nicht darauf ausgelegt, mit Datens\u00e4tzen dieser Gr\u00f6\u00dfe (Dutzende von Bytes) zu arbeiten, der Kompressionsfaktor wird eindeutig kleiner als 1 sein (was bedeutet, dass das Datenvolumen ansteigt statt sinkt);<\/li>\n<li>F\u00fchren Sie nach jedem Datensatz ein FLUSH durch.<br \/>\nViele Kompressionsbibliotheken unterst\u00fctzen FLUSH. Dies ist ein Befehl (oder ein Parameter f\u00fcr den Kompressionsprozess), der es dem Archivierungsprogramm erm\u00f6glicht, einen komprimierten Strom zu bilden, sodass auf dessen Grundlage <strong>alle<\/strong> unkomprimierten Daten, die bereits empfangen wurden, wiederhergestellt werden k\u00f6nnen. Dies istanalog zu <code>sync<\/code> in Dateisystemen oder <code>commit<\/code> in SQL.<br \/>\nWichtig ist, dass nachfolgende Komprimierungsoperationen das angeh\u00e4ufte W\u00f6rterbuch nutzen k\u00f6nnen und der Kompressionsgrad nicht so stark leidet wie im vorherigen Fall.<\/li>\n<\/ol>\n<p><\/p>\n<p>Ich denke, es ist offensichtlich, dass ich die dritte Option gew\u00e4hlt habe, lassen Sie uns n\u00e4her darauf eingehen.<\/p>\n<p><\/p>\n<p>Es wurde <noindex><a rel=\"nofollow\" href=\"https:\/\/www.bolet.org\/~pornin\/deflate-flush.html\">ein ausgezeichneter Artikel<\/a><\/noindex> \u00fcber FLUSH in zlib gefunden.<\/p>\n<p><\/p>\n<p>Ich habe basierend auf dem Artikel einen Test durchgef\u00fchrt, ich habe 70.000 Protokolleintr\u00e4ge von einem realen Ger\u00e4t mit einer Seitengr\u00f6\u00dfe von 60 KB genommen <em>(zur Seitengr\u00f6\u00dfe werden wir noch zur\u00fcckkommen)<\/em> erhalten:<\/p>\n<p><\/p>\n<p>Rohdaten<br \/>\ngzip-Kompression -9 (ohne FLUSH)<br \/>\nzlib mit Z_PARTIAL_FLUSH<br \/>\nzlib mit Z_SYNC_FLUSH<\/p>\n<p><strong>Gr\u00f6\u00dfe, KB<\/strong><br \/>\n1692<br \/>\n40<br \/>\n352<br \/>\n604<\/p>\n<p><\/p>\n<p>Auf den ersten Blick scheint der Preis, den FLUSH verlangt, \u00fcbertrieben hoch zu sein, jedoch ist unsere Auswahl tats\u00e4chlich begrenzt \u2013 entweder gar nicht komprimieren oder effektiv mit FLUSH komprimieren. Man darf nicht vergessen, dass wir 70.000 Datens\u00e4tze haben, und die durch Z_PARTIAL_FLUSH eingef\u00fchrte Redundanz betr\u00e4gt nur 4-5 Byte pro Datensatz. Und das Kompressionsverh\u00e4ltnis lag bei fast 5:1, was ein hervorragendes Ergebnis ist.<\/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>Im Fall der Verwendung von Z_SYNC_FLUSH werden die letzten 4 Bytes jedes Datensatzes immer 0x00, 0x00, 0xff, 0xff sein. Wenn wir diese kennen, k\u00f6nnen wir sie weglassen, wodurch die endg\u00fcltige Gr\u00f6\u00dfe nur 324 KB betr\u00e4gt.<\/p>\n<p><\/p>\n<p>Der Artikel, auf den ich mich beziehe, enth\u00e4lt eine Erkl\u00e4rung:<\/p>\n<p><\/p>\n<blockquote><p>Ein neuer Typ-0-Block mit leeren Inhalten wird angeh\u00e4ngt.<\/p>\n<p>Ein Typ-0-Block mit leeren Inhalten besteht aus:<\/p>\n<ul>\n<li>dem dreibitigen Blockkopf;<\/li>\n<li>0 bis 7 Bits, die gleich null sind, um die Byte-Ausrichtung zu erreichen;<\/li>\n<li>der vier Byte langen Sequenz 00 00 FF FF.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Wie man leicht merkt, kommen im letzten Block vor diesen 4 Bytes zwischen 3 und 10 Null-Bits vor. Die Praxis hat jedoch gezeigt, dass es in Wirklichkeit mindestens 10 Null-Bits sind.<\/p>\n<p><\/p>\n<p>Es stellt sich heraus, dass so kurze Datenbl\u00f6cke normalerweise (immer?) mit einem Block vom Typ 1 (fester Block) kodiert werden, der unbedingt mit 7 Null-Bits endet. Insgesamt erhalten wir 10-17 garantiert nullen Bits (und die restlichen werden mit einer Wahrscheinlichkeit von etwa 50% null sein).<\/p>\n<p><\/p>\n<p>Bei den Testdaten wird in 100% der F\u00e4lle vor 0x00, 0x00, 0xff, 0xff ein Null-Byte angezeigt, und in mehr als einem Drittel der F\u00e4lle \u2014 zwei Null-Bytes. <em>(Vielleicht liegt es daran, dass ich bin\u00e4ren CBOR verwende, w\u00e4hrend beim Einsatz von textuellem JSON h\u00e4ufiger Bl\u00f6cke vom Typ 2 \u2014 dynamischer Block \u2014 vorkommen w\u00fcrden, entsprechend w\u00fcrden Bl\u00f6cke ohne zus\u00e4tzliche Null-Bytes vor 0x00, 0x00, 0xff, 0xff auftreten.)<\/em>.<\/p>\n<p><\/p>\n<p>Insgesamt kann man mit den vorhandenen Testdaten in weniger als 250KB komprimierten Daten auskommen.<\/p>\n<p><\/p>\n<p>Man kann noch etwas mehr sparen, indem man mit den Bits jongliert: Derzeit ignorieren wir die Existenz mehrerer Null-Bits am Ende des Blocks, einige Bits am Anfang des Blocks \u00e4ndern sich ebenfalls nicht\u2026<br \/>\nAber hier habe ich die Entscheidung getroffen, aufzuh\u00f6ren, denn bei solchem Tempo k\u00f6nnte ich zur Entwicklung meines eigenen Archivierers gelangen.<\/p>\n<p><\/p>\n<p>Insgesamt habe ich aus meinen Testdaten 3-4 Bytes pro Aufzeichnung erhalten, und das Kompressionsverh\u00e4ltnis betrug mehr als 6:1. Ehrlich gesagt hatte ich nicht mit einem solchen Ergebnis gerechnet; meiner Meinung nach ist alles, was besser als 2:1 ist, bereits ein Ergebnis, das den Einsatz von Kompression rechtfertigt.<\/p>\n<p><\/p>\n<p>Alles prima, aber zlib (deflate) ist schlie\u00dflich ein bew\u00e4hrter, wenn auch etwas altmodischer Kompressionsalgorithmus. Allein die Tatsache, dass die letzten 32 KB aus dem unkomprimierten Datenstrom als W\u00f6rterbuch verwendet werden, wirkt heute seltsam (das hei\u00dft, wenn ein Datenblock sehr \u00e4hnlich dem ist, was vor 40 KB im Eingangsstrom war, wird er neu archiviert und nicht auf den vorherigen Auftritt verwiesen). In modernen Komprimierungsprogrammen wird die Gr\u00f6\u00dfe des W\u00f6rterbuchs h\u00e4ufiger in Megabytes und nicht in Kilobytes gemessen.<\/p>\n<p><\/p>\n<p>Lassen Sie uns also unsere kleine Untersuchung der Archivierungsprogramme fortsetzen.<\/p>\n<p><\/p>\n<p>Als N\u00e4chstes wurde bzip2 getestet (ich erinnere daran, dass es ohne FLUSH eine fantastische Kompressionsrate von fast 100:1 zeigte). Leider schnitt es mit FLUSH sehr schlecht ab, die Gr\u00f6\u00dfe der komprimierten Daten war gr\u00f6\u00dfer als die der unkomprimierten.<\/p>\n<p>\n<b class=\"spoiler_title\">Meine Vermutungen \u00fcber die Gr\u00fcnde f\u00fcr das Versagen<\/b><\/p>\n<p>Libbz2 bietet nur eine Option f\u00fcr den Flush an, die anscheinend das W\u00f6rterbuch leert (\u00e4hnlich wie Z_FULL_FLUSH in zlib). Von einer effektiven Kompression danach kann man nicht sprechen.<\/p>\n<p><\/p>\n<p>Der letzte Test war mit zstd. Je nach Parametern komprimiert es entweder auf Gzip-Niveau, aber viel schneller, oder besser als 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>Ich <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/facebook\/zstd\/issues\/900\">habe eine Frage<\/a><\/noindex> auf der Projektseite in Github gestellt und die Antwort erhalten, dass man mit bis zu 10 Byte Metadaten pro Block komprimierter Daten rechnen sollte, was den erhaltenen Ergebnissen nahe kommt. Deflate wird dadurch nicht eingeholt.<\/p>\n<p><\/p>\n<p>Damit habe ich beschlossen, meine Experimente mit Archivierern zu beenden (zur Erinnerung: xz, lzip, lzo, lz4 haben sich bereits in der Testphase ohne FLUSH nicht bew\u00e4hrt, und ich habe es nicht gewagt, exotischere Algorithmen zu betrachten).<\/p>\n<p><\/p>\n<p>Kehren wir zu den Problemen der Archivierung zur\u00fcck.<\/p>\n<p><\/p>\n<p>Das zweite Problem (in der Reihenfolge, nicht in der Bedeutung) besteht darin, dass komprimierte Daten einen einheitlichen Stream darstellen, in dem st\u00e4ndig Verweise auf vorherige Abschnitte erfolgen. Daher verlieren wir bei einer Besch\u00e4digung eines Teils der komprimierten Daten nicht nur den zugeh\u00f6rigen Block unkomprimierter Daten, sondern auch alle nachfolgenden.<\/p>\n<p><\/p>\n<p>Es gibt Ans\u00e4tze zur L\u00f6sung dieses Problems:<\/p>\n<p><\/p>\n<ol>\n<li>Probleme proaktiv zu vermeiden \u2013 durch Hinzuf\u00fcgen von Redundanz in die komprimierten Daten, die es erm\u00f6glicht, Fehler zu erkennen und zu beheben; dazu werden wir sp\u00e4ter mehr sagen;<\/li>\n<li>Die Auswirkungen im Falle eines Problems zu minimieren.<br \/>\nWir haben bereits erw\u00e4hnt, dass jeder Datenblock unabh\u00e4ngig komprimiert werden kann, wodurch das Problem von selbst gel\u00f6st wird (die Besch\u00e4digung der Daten eines Blocks f\u00fchrt nur zum Verlust dieser Blockdaten). Dies ist jedoch der Extremfall, bei dem die Datenkomprimierung ineffektiv ist. Das andere Extrem w\u00e4re, die gesamten 4 MB unseres Chips als ein einziges Archiv zu verwenden, was eine hervorragende Komprimierung bieten w\u00fcrde, jedoch katastrophale Folgen im Falle von Datenbesch\u00e4digung h\u00e4tte.<br \/>\n<em>Ja, es ist notwendig, einen Kompromiss in Bezug auf die Zuverl\u00e4ssigkeit einzugehen. Aber wir m\u00fcssen bedenken, dass wir ein Datenformat f\u00fcr nichtfl\u00fcchtigen Speicher mit extrem niedrigem BER und einer angegebenen Datenhaltbarkeit von 20 Jahren entwickeln.<\/em><\/li>\n<\/ol>\n<p><\/p>\n<p>In den Experimenten habe ich festgestellt, dass merkliche Verluste des Kompressionsniveaus bei komprimierten Datenbl\u00f6cken von weniger als 10 KB beginnen.<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>Das hei\u00dft, die minimal angemessene Seitengr\u00f6\u00dfe betr\u00e4gt 16 KB (einschlie\u00dflich Platz f\u00fcr Verwaltungsinformationen). Ein so kleines Seitenformat bringt jedoch erhebliche Einschr\u00e4nkungen f\u00fcr die maximale Datensatzgr\u00f6\u00dfe mit sich.<\/p>\n<p><\/p>\n<p>Obwohl ich momentan keine Datens\u00e4tze von mehr als einem Kilobyte in komprimierter Form erwarte, habe ich beschlossen, Seiten mit einer Gr\u00f6\u00dfe von 32 KB zu verwenden (insgesamt 128 Seiten pro Chip).<\/p>\n<p><\/p>\n<p><strong>Zusammenfassung:<\/strong><\/p>\n<p><\/p>\n<ul>\n<li>Wir speichern die Daten komprimiert mit zlib (deflate);<\/li>\n<li>F\u00fcr jeden Datensatz setzen wir Z_SYNC_FLUSH;<\/li>\n<li>Bei jedem komprimierten Datensatz schneiden wir die Endbytes ab <em>(z.B. 0x00, 0x00, 0xff, 0xff)<\/em>; im Header geben wir an, wie viele Bytes wir entfernt haben;<\/li>\n<li>Die Daten werden in Seiten mit 32 KB gespeichert; innerhalb einer Seite gibt es einen kontinuierlichen Fluss komprimierter Daten; bei jeder Seite beginnen wir die Komprimierung erneut.<\/li>\n<\/ul>\n<p><\/p>\n<p>Und bevor wir mit der Komprimierung abschlie\u00dfen, m\u00f6chte ich darauf hinweisen, dass wir nur mehrere Bytes komprimierter Daten pro Aufzeichnung erhalten, weshalb es \u00e4u\u00dferst wichtig ist, die Metadaten nicht aufzubl\u00e4hen; jeder Byte z\u00e4hlt hier.<\/p>\n<p><\/p>\n<h3 id=\"hranenie-zagolovkov-dannyh\">Speicherung von Daten\u00fcberschriften<\/h3>\n<p><\/p>\n<p>Da wir Aufzeichnungen variabler L\u00e4nge haben, m\u00fcssen wir irgendwie die Platzierung\/Grenzen der Aufzeichnungen bestimmen.<\/p>\n<p><\/p>\n<p>Ich kenne drei Ans\u00e4tze:<\/p>\n<p><\/p>\n<ol>\n<li>Alle Aufzeichnungen werden in einem kontinuierlichen Fluss gespeichert, zuerst kommt der Header der Aufzeichnung, der die L\u00e4nge enth\u00e4lt, und dann die eigentliche Aufzeichnung.<br \/>\nIn dieser Variante k\u00f6nnen sowohl die Header als auch die Daten eine variable L\u00e4nge haben.<br \/>\nIm Wesentlichen haben wir eine einfach verkettete Liste, die h\u00e4ufig verwendet wird;<\/li>\n<li>Die Header und die eigentlichen Aufzeichnungen werden in separaten Fl\u00fcssen gespeichert.<br \/>\nDurch die Verwendung von Headern fester L\u00e4nge stellen wir sicher, dass die Besch\u00e4digung eines Headers die anderen nicht beeinflusst.<br \/>\nEin \u00e4hnlicher Ansatz wird beispielsweise in vielen Dateisystemen verwendet;<\/li>\n<li>Die Eintr\u00e4ge werden in einem kontinuierlichen Stream gespeichert, die Grenze eines Eintrags wird durch ein bestimmtes Marker (ein Zeichen\/eine Zeichenfolge, die innerhalb von Datenbl\u00f6cken nicht erlaubt ist) definiert. Wenn innerhalb eines Eintrags ein Marker auftaucht, wird dieser durch eine bestimmte Sequenz ersetzt (wir escapen ihn).<br \/>\nEin \u00e4hnlicher Ansatz wird beispielsweise im PPP-Protokoll verwendet.<\/li>\n<\/ol>\n<p><\/p>\n<p>Ich werde dies veranschaulichen.<\/p>\n<p><\/p>\n<p>Variante 1:<br \/>\n<img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/e5a9676ca21eaca07e64ddf9fcd9f2eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nHier ist alles ganz einfach: Wenn wir die L\u00e4nge des Eintrags kennen, k\u00f6nnen wir die Adresse des n\u00e4chsten Headers berechnen. So bewegen wir uns durch die Header, bis wir einen Bereich erreichen, der mit 0xff gef\u00fcllt ist (freien Bereich) oder das Ende der Seite.<\/p>\n<p><\/p>\n<p>Variante 2:<br \/>\n<img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im 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>Variante 3:<br \/>\n<img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/dfb64009aa7781fcb8a47423ff4160c6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nEs ist nicht notwendig, die L\u00e4nge oder andere Informationen \u00fcber den Datenstandort im Header zu speichern; Marker, die die Grenzen der Eintr\u00e4ge anzeigen, sind ausreichend. Die Daten m\u00fcssen jedoch beim Schreiben\/Lesen verarbeitet werden.<br \/>\nAls Marker w\u00fcrde ich 0xff verwenden (das nach dem L\u00f6schen die Seite f\u00fcllt), somit wird der freie Bereich nicht f\u00e4lschlicherweise als Daten interpretiert.<\/p>\n<p><\/p>\n<p>Vergleichstabelle:<\/p>\n<p><\/p>\n<p>Option 1<br \/>\nOption 2<br \/>\nOption 3<\/p>\n<p><strong>Fehlerresistenz<\/strong><br \/>\n&#8212;<br \/>\n+<br \/>\n+<\/p>\n<p><strong>Kompaktheit<\/strong><br \/>\n+<br \/>\n&#8212;<br \/>\n+<\/p>\n<p><strong>Implementierungsaufwand<\/strong><br \/>\n*<br \/>\n**<br \/>\n**<\/p>\n<p><\/p>\n<p>Option 1 hat einen gravierenden Nachteil: Bei einer Besch\u00e4digung eines der Header ist die gesamte nachfolgende Kette zerst\u00f6rt. Die anderen Optionen erm\u00f6glichen die Wiederherstellung eines Teils der Daten, selbst bei massiven Besch\u00e4digungen.<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>Kompaktheit:<\/p>\n<p><\/p>\n<ul>\n<li>Im ersten Ansatz m\u00fcssen wir nur die L\u00e4nge im Header speichern; wenn wir Variablen mit variabler L\u00e4nge verwenden, k\u00f6nnen wir in den meisten F\u00e4llen mit nur einem Byte auskommen.<\/li>\n<li>Im zweiten Ansatz m\u00fcssen wir die Anfangsadresse und die L\u00e4nge speichern; der Datensatz sollte eine konstante Gr\u00f6\u00dfe haben, ich sch\u00e4tze 4 Byte pro Datensatz (zwei Byte f\u00fcr den Offset und zwei Byte f\u00fcr die L\u00e4nge);<\/li>\n<li>F\u00fcr die dritte Variante gen\u00fcgt ein einziges Zeichen, um den Beginn des Datensatzes zu kennzeichnen, zus\u00e4tzlich wird sich der Datensatz aufgrund der Escape-Codierung um 1-2% erh\u00f6hen. Insgesamt ein ziemlich \u00e4hnliches Verh\u00e4ltnis wie im ersten Ansatz.<\/li>\n<\/ul>\n<p><\/p>\n<p>Urspr\u00fcnglich betrachtete ich die zweite Variante als Hauptoption (und habe sogar eine Implementierung geschrieben). Ich habe sie nur verworfen, als ich mich endg\u00fcltig f\u00fcr die Nutzung von Kompression entschied.<\/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>Was die dritte Variante betrifft: Ich habe ihr zwei Sterne f\u00fcr die Komplexit\u00e4t der Implementierung gegeben, einfach weil ich nicht gerne mit Escape-Codierungen, der \u00c4nderung der L\u00e4nge w\u00e4hrend des Prozesses usw. herumspiele. Ja, das ist vielleicht voreingenommen, aber ich bin es, der den Code schreiben muss \u2013 warum sollte ich mich zwingen, etwas zu tun, was mir nicht gef\u00e4llt.<\/p>\n<p><\/p>\n<p><strong>Zusammenfassung:<\/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\">Verwendung von Bitfeldern zur Kontrolle des Erfolgs von Schreiboperationen.<\/h3>\n<p><\/p>\n<p>Ich erinnere mich nicht mehr, wo ich die Idee aufgeschnappt habe, aber es sieht etwa so aus:<br \/>\nF\u00fcr jeden Eintrag reservieren wir einige Bits zur Speicherung von Flags.<br \/>\n<em>Wie bereits erw\u00e4hnt, sind nach dem L\u00f6schen alle Bits mit 1 gef\u00fcllt, und wir k\u00f6nnen 1 in 0 \u00e4ndern, aber nicht umgekehrt.<\/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>So k\u00f6nnte die Speicherung eines variablen L\u00e4ngeneintrags im Flash aussehen:<\/p>\n<p><\/p>\n<ol>\n<li>Wir setzen das Flag 'Schreiben von L\u00e4nge begonnen';<\/li>\n<li>Wir schreiben die L\u00e4nge;<\/li>\n<li>Wir setzen das Flag 'Schreiben von Daten begonnen';<\/li>\n<li>Wir schreiben die Daten;<\/li>\n<li>Wir setzen das Flag 'Schreiben abgeschlossen'.<\/li>\n<\/ol>\n<p><\/p>\n<p>Zus\u00e4tzlich haben wir ein Flag 'Fehler aufgetreten', insgesamt also 4 Bit-Flags.<\/p>\n<p><\/p>\n<p>In diesem Fall haben wir zwei stabile Zust\u00e4nde '1111' - Schreiben nicht begonnen und '1000' - Schreiben war erfolgreich; bei einer unerwarteten Unterbrechung des Schreibprozesses erhalten wir Zwischenzust\u00e4nde, die wir sp\u00e4ter erkennen und bearbeiten k\u00f6nnen.<\/p>\n<p><\/p>\n<p>Der Ansatz ist interessant, sch\u00fctzt jedoch nur vor pl\u00f6tzlichem Stromausfall und \u00e4hnlichen St\u00f6rungen, was nat\u00fcrlich wichtig ist, aber bei weitem nicht der einzige (und sogar nicht der Haupt-) Grund f\u00fcr m\u00f6gliche Ausf\u00e4lle.<\/p>\n<p><\/p>\n<p><strong>Zusammenfassung:<\/strong> Lassen Sie uns weiter auf der Suche nach einer guten L\u00f6sung gehen.<\/p>\n<p><\/p>\n<h3 id=\"kontrolnye-summy\">Pr\u00fcfziffern<\/h3>\n<p><\/p>\n<p>Pr\u00fcfziffern erm\u00f6glichen es zudem, mit ausreichender Wahrscheinlichkeit zu best\u00e4tigen, dass wir genau das lesen, was h\u00e4tte aufgezeichnet werden sollen. Im Gegensatz zu den oben betrachteten Bitfeldern funktionieren sie immer.<\/p>\n<p><\/p>\n<p>Wenn wir die Liste potenzieller Problemquellen betrachten, die wir zuvor erw\u00e4hnt haben, kann die Pr\u00fcfziffer einen Fehler unabh\u00e4ngig von seiner Herkunft erkennen. <em>(au\u00dfer vielleicht sch\u00e4dlichen Au\u00dferirdischen \u2013 die k\u00f6nnen auch die Pr\u00fcfziffer f\u00e4lschen)<\/em>.<\/p>\n<p><\/p>\n<p>Wenn unser Ziel darin besteht, zu \u00fcberpr\u00fcfen, dass die Daten intakt sind, sind Pr\u00fcfziffern eine ausgezeichnete Idee.<\/p>\n<p><\/p>\n<p>Die Auswahl des Algorithmus zur Berechnung der Pr\u00fcfziffer war unkritisch \u2013 CRC. Einerseits erm\u00f6glichen die mathematischen Eigenschaften eine 100%ige Erkennung bestimmter Fehlerarten, andererseits zeigt dieser Algorithmus bei zuf\u00e4lligen Daten normalerweise eine Kollisionswahrscheinlichkeit, die kaum \u00fcber dem theoretischen Limit liegt. <img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/bf9cca3564db7d9d03d3ce49642d3a71.jpg\" style=\"display:block;margin: 0 auto;\" \/>Obwohl dies nicht der schnellste Algorithmus ist und nicht immer die minimalste Anzahl an Kollisionsf\u00e4llen aufweist, hat er eine sehr wichtige Eigenschaft: In den Tests, die ich gesehen habe, sind mir keine Muster begegnet, bei denen er eindeutig versagt hat. Stabilit\u00e4t ist in diesem Fall das Hauptmerkmal.<\/p>\n<p><\/p>\n<p>Beispiel einer umfangreichen Untersuchung: <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo.html\">Teil 1<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo2.html\">Teil 2<\/a><\/noindex> <em>(Links zu narod.ru, Entschuldigung)<\/em>.<\/p>\n<p><\/p>\n<p>Die Auswahl der Pr\u00fcfziffer ist jedoch nicht abgeschlossen, CRC ist eine gesamte Familie von Pr\u00fcfziffern. Es muss eine L\u00e4nge festgelegt werden, bevor ein Polynom ausgew\u00e4hlt werden kann.<\/p>\n<p><\/p>\n<p>Die Wahl der Pr\u00fcfziffernl\u00e4nge ist nicht so einfach, wie es auf den ersten Blick scheint.<\/p>\n<p><\/p>\n<p>Ich illustriere das:<br \/>\nNehmen wir an, die Fehlerwahrscheinlichkeit liegt bei jedem Byte <img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/89a9be2edf2a43cc9115eba37fa02fc8.jpg\" style=\"display:block;margin: 0 auto;\" \/> und eine ideale Pr\u00fcfziffer, berechnen wir die durchschnittliche Anzahl der Fehler pro Millionen Eintr\u00e4ge:<\/p>\n<p><\/p>\n<p>Daten, Byte<br \/>\nPr\u00fcfziffer, Byte<br \/>\nNicht erkannte Fehler<br \/>\nFalsche Fehlererkennung<br \/>\nInsgesamt falsche Ausl\u00f6sungen<\/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>Es scheint ganz einfach zu sein \u2013 w\u00e4hle je nach L\u00e4nge der zu sch\u00fctzenden Daten die L\u00e4nge der Pr\u00fcfziffer mit minimalen Fehlalarmen \u2013 und alles ist erledigt.<\/p>\n<p><\/p>\n<p>Allerdings gibt es bei kurzen Pr\u00fcfziffern ein Problem: Sie erkennen zwar einzelne Bitfehler gut, k\u00f6nnten jedoch mit einer relativ hohen Wahrscheinlichkeit zuf\u00e4llige Daten als korrekt werten. Auf Habr gab es bereits einen Artikel, der dieses Thema behandelt hat. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/428746\/\">das Problem in der realen Welt<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Um zuf\u00e4llige \u00dcbereinstimmungen der Pr\u00fcfziffer nahezu unm\u00f6glich zu machen, sollten Pr\u00fcfziffern mit einer L\u00e4nge von 32 Bit oder mehr verwendet werden. <em>(F\u00fcr L\u00e4ngen \u00fcber 64 Bit werden normalerweise kryptographische Hash-Funktionen verwendet.)<\/em>.<\/p>\n<p><\/p>\n<p>Obwohl ich zuvor erw\u00e4hnt habe, dass wir Platz sparen sollten, werden wir dennoch eine 32-Bit-Pr\u00fcfziffer verwenden (16 Bit sind zu wenig, die Wahrscheinlichkeit einer Kollision liegt \u00fcber 0,01%; und 24 Bit sind, wie man so sagt, weder Fisch noch Fleisch).<\/p>\n<p><\/p>\n<p>Hier k\u00f6nnte man einwenden: Haben wir jeden Byte beim Komprimieren gespart, nur um jetzt sofort 4 Bytes zu verlieren? W\u00e4re es nicht besser gewesen, gar nicht zu komprimieren und keine Pr\u00fcfziffer hinzuzuf\u00fcgen? Nat\u00fcrlich nicht, das Fehlen von Kompression <em>bedeutet<\/em>, dass die Integrit\u00e4tspr\u00fcfung f\u00fcr uns nicht notwendig ist.<\/p>\n<p><\/p>\n<p>Wir wollen kein Rad neu erfinden, wenn es um die Auswahl des Polynoms geht, also nehmen wir das derzeit beliebte CRC-32C.<br \/>\nDieser Code erkennt 6 Bitfehler in Paketen bis zu 22 Bytes (wohl der h\u00e4ufigste Fall f\u00fcr uns), 4 Bitfehler in Paketen bis zu 655 Bytes (auch ein h\u00e4ufiges Szenario f\u00fcr uns) und 2 oder jede ungerade Anzahl von Bitfehlern in Paketen beliebiger sinnvoller L\u00e4nge.<\/p>\n<p>\n<b class=\"spoiler_title\">Falls jemand an den Details interessiert ist,<\/b><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Cyclic_redundancy_check\">schaut euch den Wikipedia-Artikel<\/a><\/noindex> \u00fcber CRC an.<\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0x8f6e37a0_len.txt\">Die Parametrierung des crc-32c-Codes<\/a><\/noindex> findet man <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/crc\/notes.html\">auf Kupman\u2019s Webseite,<\/a><\/noindex> dem wohl f\u00fchrenden Spezialisten f\u00fcr CRC auf diesem Planeten.<\/p>\n<p><\/p>\n<p>In <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/networks\/dsn02\/dsn02_koopman.pdf\">In seinem Artikel<\/a><\/noindex> gibt es <noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0xfa567d89_len.txt\">noch einen interessanten Code,<\/a><\/noindex>der etwas bessere Parameter f\u00fcr die f\u00fcr uns relevanten Paketl\u00e4ngen bietet, aber ich halte den Unterschied nicht f\u00fcr entscheidend und erachte mich als kompetent genug, um einen benutzerdefinierten Code anstelle eines standardisierten und gut erforschten zu w\u00e4hlen.<\/p>\n<p><\/p>\n<p>Au\u00dferdem, da unsere Daten komprimiert sind, stellt sich die Frage: Soll die Pr\u00fcfziffer auf komprimierten oder unkomprimierten Daten berechnet werden?<\/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>Letztendlich m\u00fcssen wir die Datenintegrit\u00e4t pr\u00fcfen \u2013 genau das tun wir hier direkt (wobei gleichzeitig m\u00f6gliche Fehler bei der Kompression\/Dekompression und durch defekten Speicher \u00fcberpr\u00fcft werden);<\/li>\n<li>Der Deflate-Algorithmus in zlib hat eine recht ausgereifte Implementierung und <em>sollte<\/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>Ich finde die Idee, dem Dekompressor potenziell fehlerhafte Daten zu \u00fcbergeben, nicht besonders gut, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.cvedetails.com\/vulnerability-list\/vendor_id-72\/product_id-1820\/GNU-Zlib.html\">wer wei\u00df schon<\/a><\/noindex>, wie er reagieren wird.<\/li>\n<\/ul>\n<p><\/p>\n<p>In diesem Projekt habe ich mich entschieden, von der g\u00e4ngigen Praxis abzuweichen, die Pr\u00fcfziffer unkomprimierter Daten zu speichern.<\/p>\n<p><\/p>\n<p><strong>Zusammenfassung:<\/strong> Wir verwenden CRC-32C, wobei die Pr\u00fcfziffer von den Daten in der Form berechnet wird, in der sie im Flash gespeichert werden (nach der Kompression).<\/p>\n<p><\/p>\n<h3 id=\"izbytochnost\">Redundanz<\/h3>\n<p><\/p>\n<p>Der Einsatz von Redundanzkodierung kann zwar nicht verhindern, dass Daten verloren gehen, jedoch kann er die Wahrscheinlichkeit eines irreversiblen Datenverlusts erheblich (oft um viele Gr\u00f6\u00dfenordnungen) verringern.<\/p>\n<p><\/p>\n<p>Wir k\u00f6nnen verschiedene Arten von Redundanz verwenden, um Fehler zu korrigieren.<br \/>\nHamming-Codes k\u00f6nnen Einzelbitfehler korrigieren, Reed-Solomon-Codes sind zeichenbasiert, mehrere Datenkopien zusammen mit Pr\u00fcfziffern oder Codes wie RAID-6 k\u00f6nnen helfen, Daten selbst im Falle massiver Besch\u00e4digungen wiederherzustellen.<br \/>\nAnfangs war ich auf eine breite Anwendung von fehlerresistenten Codierungen eingestellt, aber dann wurde mir klar, dass man zuerst verstehen muss, vor welchen Fehlern man sich sch\u00fctzen m\u00f6chte, bevor man eine Codierung w\u00e4hlt.<\/p>\n<p><\/p>\n<p>Wir haben bereits vorher dar\u00fcber gesprochen, dass Fehler so schnell wie m\u00f6glich erkannt werden sollten. In welchen Situationen k\u00f6nnten wir auf Fehler sto\u00dfen?<\/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 \/>\nLeider bleibt bei einem solchen Fehler nur, ung\u00fcltige Eintr\u00e4ge zu ignorieren und die Daten als verloren zu betrachten;<\/li>\n<li>Schreibfehler (aus irgendwelchen Gr\u00fcnden wurde nicht das geschrieben, was beabsichtigt war)<br \/>\nSolche Fehler k\u00f6nnen wir sofort erkennen, wenn wir direkt nach dem Schreiben eine Kontrolllesung durchf\u00fchren;<\/li>\n<li>Datenverzerrung im Speicher w\u00e4hrend der Speicherung;<\/li>\n<li>Lese-Fehler<br \/>\nUm diesen zu beheben, reicht es aus, im Falle einer fehlerhaften Pr\u00fcfziffer das Lesen mehrmals zu wiederholen.<\/li>\n<\/ol>\n<p><\/p>\n<p>Das hei\u00dft, nur Fehler des dritten Typs (selbstst\u00e4ndige Datenbesch\u00e4digung w\u00e4hrend der Speicherung) k\u00f6nnen ohne fehlerresistente Codierung nicht behoben werden. Es ist jedoch anzunehmen, dass solche Fehler \u00e4u\u00dferst unwahrscheinlich sind.<\/p>\n<p><\/p>\n<p><strong>Zusammenfassung:<\/strong> Es wurde beschlossen, auf \u00fcberfl\u00fcssige Codierung zu verzichten. Sollte sich in der Praxis herausstellen, dass diese Entscheidung fehlerhaft ist, wird die Frage erneut betrachtet (mit bereits gesammelten Statistiken \u00fcber Ausf\u00e4lle, die die Auswahl der optimalen Codierungsart erm\u00f6glichen).<\/p>\n<p><\/p>\n<h3 id=\"prochee\">Sonstiges<\/h3>\n<p><\/p>\n<p>Nat\u00fcrlich erlaubt das Format des Artikels nicht, jeden einzelnen Bit im Format zu begr\u00fcnden. <em>(und meine Kr\u00e4fte sind auch bereits ersch\u00f6pft)<\/em>, deshalb gehe ich kurz auf einige Punkte ein, die bisher nicht angesprochen wurden.<\/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 \/>\nDas bedeutet, es wird keine speziellen Seiten mit Metadaten, separaten Streams usw. geben, stattdessen einen einheitlichen Stream, der alle Seiten nacheinander \u00fcberschreibt.<br \/>\nDies gew\u00e4hrleistet einen gleichm\u00e4\u00dfigen Verschlei\u00df der Seiten, das Fehlen eines einzelnen Ausfallpunkts und es gef\u00e4llt einfach;<\/li>\n<li>Es muss unbedingt eine Versionskontrolle des Formats eingeplant werden.<br \/>\nEin Format ohne Versionsnummer im Header ist schlecht!<br \/>\nEs reicht, im Header der Seite ein Feld mit einer Art Magic Number (Signatur) hinzuzuf\u00fcgen, die auf die verwendete Formatversion hinweist. <em>(ich glaube nicht, dass es in der Praxis sogar zehn davon geben wird)<\/em>;<\/li>\n<li>F\u00fcr die Datens\u00e4tze (von denen es sehr viele gibt) einen variabel langen Header verwenden, wobei man in den meisten F\u00e4llen eine L\u00e4nge von 1 Byte anstrebt;<\/li>\n<li>Zum Kodieren der Headerl\u00e4nge und der L\u00e4nge des gek\u00fcrzten Teils der kompakten Aufzeichnung sollten bin\u00e4re variabel lange Codes verwendet werden.<\/li>\n<\/ul>\n<p><\/p>\n<p>Sehr hilfreich war <noindex><a rel=\"nofollow\" href=\"https:\/\/planetcalc.com\/2481\/\">der Online-Generator<\/a><\/noindex> von Huffman-Codes. In nur wenigen Minuten gelang es, die ben\u00f6tigten variabel langen Codes zu finden.<\/p>\n<p><\/p>\n<h1 id=\"anchorformatanchoropisanie-formata-hraneniya-dannyh\"><noindex><a rel=\"nofollow\" name=\"format\"><\/a><\/noindex>Beschreibung des Datenformat-Speichers<\/h1>\n<p><\/p>\n<h2 id=\"byte-order\">Byte-Reihenfolge<\/h2>\n<p><\/p>\n<p>Felder, die gr\u00f6\u00dfer als ein Byte sind, werden im Big-Endian-Format (Netzwerk-Byte-Reihenfolge) gespeichert, das hei\u00dft 0x1234 wird als 0x12, 0x34 aufgezeichnet.<\/p>\n<p><\/p>\n<h2 id=\"delenie-na-stranicy\">Seitenaufteilung<\/h2>\n<p><\/p>\n<p>Der gesamte Flash-Speicher ist in Seiten gleicher Gr\u00f6\u00dfe unterteilt.<\/p>\n<p><\/p>\n<p>Die Standardgr\u00f6\u00dfe der Seite betr\u00e4gt 32 KB, jedoch nicht mehr als 1\/4 der Gesamtgr\u00f6\u00dfe des Speicherchips (bei einem 4 MB-Chip ergibt dies 128 Seiten).<\/p>\n<p><\/p>\n<p>Jede Seite speichert Daten unabh\u00e4ngig von anderen Seiten (die Daten einer Seite verweisen nicht auf die Daten einer anderen Seite).<\/p>\n<p><\/p>\n<p>Alle Seiten sind in nat\u00fcrlicher Reihenfolge nummeriert (in aufsteigender Adressreihenfolge), beginnend mit der Nummer 0 (die Nullseite beginnt bei Adresse 0, die erste bei 32 KB, die zweite bei 64 KB usw.).<\/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\">Innerhalb der Seite<\/h2>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im 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>Der Seitenheader (in schmutzig-gr\u00fcn auf dem Schema) besteht aus:<\/p>\n<p><\/p>\n<ul>\n<li>einem zweibytepard Magic Number (auch bekannt als Versionsbezeichner des Formats)<br \/>\nf\u00fcr die aktuelle Formatversion wird dies berechnet als <code>0xed00 \u2295 Seitenzahl<\/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>Die Eintr\u00e4ge auf der Seite werden komprimiert gespeichert (es wird der deflate-Algorithmus verwendet). Alle Eintr\u00e4ge auf einer Seite werden in einem Durchgang komprimiert (es wird ein gemeinsames W\u00f6rterbuch eingesetzt), und f\u00fcr jede neue Seite beginnt die Kompression erneut. Das bedeutet, dass zur Dekompression eines beliebigen Eintrags alle vorherigen Eintr\u00e4ge von dieser Seite (und nur von dieser) erforderlich sind.<\/p>\n<p><\/p>\n<p>Jeder Eintrag wird mit dem Z_SYNC_FLUSH-Flag komprimiert, wobei am Ende des komprimierten Streams 4 Bytes 0x00, 0x00, 0xff, 0xff auftauchen, m\u00f6glicherweise vorangestellt von einem oder zwei Nullbytes.<br \/>\nDiese Sequenz (mit einer L\u00e4nge von 4, 5 oder 6 Bytes) wird bei der Speicherung im Flash-Speicher verworfen.<\/p>\n<p><\/p>\n<p>Der Eintragsheader besteht aus 1, 2 oder 3 Bytes, die Folgendes speichern:<\/p>\n<p><\/p>\n<ul>\n<li>ein Bit (T), das den Typ des Eintrags anzeigt: 0 \u2014 Kontext, 1 \u2014 Journal;<\/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>die L\u00e4nge des Eintrags (L).<\/li>\n<\/ul>\n<p><\/p>\n<p>Tabelle der Werte S:<\/p>\n<p><\/p>\n<p>O<br \/>\nL\u00e4nge des Headers in Byte<br \/>\nWird beim Schreiben verworfen, 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>Ich habe versucht, dies zu veranschaulichen, bin mir jedoch nicht sicher, wie klar es geworden ist:<br \/>\n<img decoding=\"async\" alt=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/8c9b739eec5af4429395c32b4ba4044a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nGelb kennzeichnet hier das Feld T, Wei\u00df das Feld S, Gr\u00fcn L (L\u00e4nge der komprimierten Daten in Byte), Blau die komprimierten Daten und Rot die letzten Bytes der komprimierten Daten, die nicht in den Flash-Speicher geschrieben werden.<\/p>\n<p><\/p>\n<p>Somit k\u00f6nnen wir die Header von der h\u00e4ufigsten L\u00e4nge (bis zu 63+5 Byte in komprimierter Form) mit einem Byte schreiben.<\/p>\n<p><\/p>\n<p>Nach jedem Eintrag wird eine CRC-32C-Pr\u00fcfziffer gespeichert, bei der als Anfangswert (init) der umgekehrte Wert der vorherigen Pr\u00fcfziffer verwendet wird.<\/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=\"Meine Implementierung eines Ringspeichers im NOR-Flash\" src=\"\/wp-content\/uploads\/2019\/12\/b194d6a3d18ae2f4ae5d4054a93c0ea3.jpg\" style=\"display:block;margin: 0 auto;\" \/>.<br \/>\nDas hei\u00dft, wir berechnen tats\u00e4chlich die CRC aller vorherigen Bytes der Header und Daten auf dieser Seite.<\/em><\/p>\n<p><\/p>\n<p>Direkt hinter der Pr\u00fcfziffer liegt der Header des n\u00e4chsten Eintrags.<\/p>\n<p><\/p>\n<p>Der Header ist so konstruiert, dass sein erster Byte immer von 0x00 und 0xff verschieden ist (trifft man beim ersten Byte des Headers auf 0xff, handelt es sich um einen ungenutzten Bereich; 0x00 signalisiert einen Fehler).<\/p>\n<p><\/p>\n<h2 id=\"primernye-algoritmy\">Beispielhafte Algorithmen<\/h2>\n<p><\/p>\n<h3 id=\"chtenie-iz-flesh-pamyati\">Lesen aus dem Flash-Speicher<\/h3>\n<p><\/p>\n<p>Jedes Lesen erfolgt mit einer \u00dcberpr\u00fcfung der Pr\u00fcfziffer.<br \/>\nStimmt die Pr\u00fcfziffer nicht \u00fcberein, wird das Lesen mehrmals wiederholt, in der Hoffnung, die korrekten Daten zu erhalten.<\/p>\n<p><\/p>\n<p><em>(Das macht Sinn, Linux cached das Lesen aus NOR Flash nicht, best\u00e4tigt)<\/em><\/p>\n<p><\/p>\n<h3 id=\"zapis-v-flesh-pamyat\">Schreiben in den Flash-Speicher<\/h3>\n<p><\/p>\n<p>Wir schreiben die Daten.<br \/>\nWir lesen sie.<\/p>\n<p><\/p>\n<p>Stimmen die gelesenen Daten nicht mit den geschriebenen \u00fcberein, f\u00fcllen wir den Bereich mit Nullen auf und signalisieren einen Fehler.<\/p>\n<p><\/p>\n<h3 id=\"podgotovka-novoy-mikroshemy-k-rabote\">Vorbereitung des neuen Chips f\u00fcr den Betrieb<\/h3>\n<p><\/p>\n<p>Zur Initialisierung wird im ersten (genauer gesagt nullten) Seiten eine Header mit Version 1 geschrieben.<br \/>\nAnschlie\u00dfend wird in diese Seite der Anfangskontext geschrieben (enth\u00e4lt die UUID des Ger\u00e4ts und die Standardkonfigurationen). <\/p>\n<p><\/p>\n<p>Alles, der Flash-Speicher ist bereit f\u00fcr den Betrieb.<\/p>\n<p><\/p>\n<h3 id=\"zagruzka-avtomata\">Starten des Ger\u00e4ts<\/h3>\n<p><\/p>\n<p>Beim Start werden die ersten 8 Bytes jeder Seite (Header + CRC) gelesen; Seiten mit unbekannter Magic Number oder fehlerhaftem CRC werden ignoriert.<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 \/>\nDas zlib-W\u00f6rterbuch wird gespeichert (es wird zum Nachtragen auf dieser Seite ben\u00f6tigt).<\/p>\n<p><\/p>\n<p>Ladevorgang abgeschlossen, Kontext wiederhergestellt, die Arbeit kann beginnen.<\/p>\n<p><\/p>\n<h3 id=\"dobavlenie-zapisi-v-zhurnal\">Eintrag zum Protokoll hinzuf\u00fcgen<\/h3>\n<p><\/p>\n<p>Wir komprimieren den Eintrag mit dem richtigen W\u00f6rterbuch und geben Z_SYNC_FLUSH an. \u00dcberpr\u00fcfen, ob der komprimierte Eintrag auf die aktuelle Seite passt.<br \/>\nWenn er nicht passt (oder wenn auf der Seite CRC-Fehler waren) \u2013 beginnen wir eine neue Seite (siehe unten).<br \/>\nWir schreiben den Eintrag und die CRC. Wenn ein Fehler aufgetreten ist \u2013 beginnen wir eine neue Seite.<\/p>\n<p><\/p>\n<h3 id=\"novaya-stranica\">Neue Seite<\/h3>\n<p><\/p>\n<p>Wir w\u00e4hlen die Seite mit der niedrigsten Nummer, die frei ist (frei ist eine Seite mit einer falschen Pr\u00fcfziffer im Header oder mit einer Version, die kleiner ist als die aktuelle). Sollte es keine solchen Seiten geben, w\u00e4hlen wir die Seite mit der niedrigsten Nummer aus den Seiten, die die gleiche Version wie die aktuelle haben.<br \/>\nWir markieren die ausgew\u00e4hlte Seite als gel\u00f6scht. Wir vergleichen den Inhalt mit 0xff. Wenn etwas nicht stimmt, w\u00e4hlen wir die n\u00e4chste freie Seite usw.<br \/>\nWir schreiben den Header auf die gel\u00f6schte Seite, die erste Eintragung ist der aktuelle Zustand des Kontextes, die n\u00e4chste - ein ungeschriebener Journaleintrag (sofern vorhanden).<\/p>\n<p><\/p>\n<h1 id=\"primenimost-formata\">Anwendbarkeit des Formats<\/h1>\n<p><\/p>\n<p>Meiner Meinung nach ist es ein brauchbares Format f\u00fcr die Speicherung beliebiger mehr oder weniger komprimierbarer Informationsstr\u00f6me (einfacher Text, JSON, MessagePack, CBOR, m\u00f6glicherweise protobuf) im 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>Es sollte nicht mit Speichermedien mit hohem BER verwendet werden, wie z.B. NAND oder MLC NOR. <em>(Gibt es solche Speicher eigentlich im Verkauf? Ich habe nur Erw\u00e4hnungen in Arbeiten \u00fcber Fehlerkorrektur gesehen.)<\/em>.<\/p>\n<p><\/p>\n<p>Zumal es nicht mit Ger\u00e4ten verwendet werden sollte, die ihre eigene FTL haben: USB-Flash, SD, MicroSD usw. <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>Je nach Anforderungen kann das Format unver\u00e4ndert auf Flash-Laufwerken von 128 Kbit (16 Kb) bis 1 Gbit (128 Mb) verwendet werden. Bei Bedarf l\u00e4sst es sich auch auf gr\u00f6\u00dferen Chips nutzen, allerdings m\u00fcsste wahrscheinlich die Seitengr\u00f6\u00dfe angepasst werden. <em>(Hier stellt sich jedoch die Frage der wirtschaftlichen Rentabilit\u00e4t, die Preise f\u00fcr gro\u00dfe NOR-Flash-Bausteine sind nicht erfreulich.)<\/em>.<\/p>\n<p><\/p>\n<p>Wenn jemand das Format interessant findet und es in einem Open-Source-Projekt verwenden m\u00f6chte \u2013 schreibt mir, ich werde versuchen, Zeit zu finden, den Code zu optimieren und auf GitHub hochzuladen.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Fazit<\/h1>\n<p><\/p>\n<p>Wie man sieht, ist das Format am Ende einfach herausgekommen <em>und sogar langweilig<\/em>.<\/p>\n<p><\/p>\n<p>In dem Artikel ist es schwierig, die Evolution meiner Meinung widerzuspiegeln, aber glauben Sie mir: Zun\u00e4chst wollte ich etwas Ma\u00dfgeschneidertes, Unverw\u00fcstliches schaffen, das selbst nach einer nuklearen Explosion in unmittelbarer N\u00e4he \u00fcberleben kann. Doch der Verstand (hoffentlich) hat schlie\u00dflich gesiegt und die Priorit\u00e4ten haben sich allm\u00e4hlich auf Einfachheit und Kompaktheit verschoben.<\/p>\n<p><\/p>\n<p>Kann es sein, dass ich mich geirrt habe? Ja, nat\u00fcrlich. Es k\u00f6nnte durchaus passieren, dass wir beispielsweise eine Charge minderwertiger Chips gekauft haben. Oder aus einem anderen Grund die Ausr\u00fcstung die Erwartungen hinsichtlich der Zuverl\u00e4ssigkeit nicht erf\u00fcllt.<\/p>\n<p><\/p>\n<p>Habe ich einen Plan f\u00fcr diesen Fall? Ich denke, nach dem Lesen des Artikels werden Sie keinen Zweifel daran haben, dass es einen Plan gibt. Und sogar mehr als einen.<\/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>Im Moment l\u00e4uft alles normal, in den n\u00e4chsten Tagen wird die L\u00f6sung implementiert. <em>(ungef\u00e4hr)<\/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>(wenn wir Pech haben, auch fr\u00fcher)<\/em>.<\/p>\n<p><\/p>\n<p>Sollten sich nach der Nutzung ernsthafte Probleme zeigen und Anpassungen erforderlich sein, werde ich unbedingt dar\u00fcber berichten.<\/p>\n<p><\/p>\n<h1 id=\"literatura\">Literatur<\/h1>\n<p><\/p>\n<p>Ich wollte keine lange, langweilige Liste verwendeter Arbeiten erstellen. Schlie\u00dflich hat jeder Google.<\/p>\n<p><\/p>\n<p>Hier habe ich beschlossen, eine Liste von Entdeckungen zu f\u00fchren, die mir besonders interessant erschienen. Im Laufe der Zeit sind sie jedoch direkt in den Text des Artikels eingeflossen, und es blieb nur ein Punkt in der Liste:<\/p>\n<p><\/p>\n<ol>\n<li>Dienstprogramm <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/madler\/infgen\/\">infgen<\/a><\/noindex> vom Autor zlib. Es kann den Inhalt von deflate\/zlib\/gzip-Archiven in verst\u00e4ndlicher Form anzeigen. Wenn Sie sich mit der internen Struktur des deflate- (oder gzip-) Formats besch\u00e4ftigen m\u00fcssen, empfehle ich es dringend.<\/li>\n<\/ol>\n<p>Quelle: <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 4.9.10 - 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. \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\" \/>\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\/de\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"de_DE\" \/>\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. \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\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/de\/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\udd47Meine Implementierung eines Ringpuffers im NOR-Flash | ProHoster","description":"Hintergrund: Es gibt selbst entwickelte Verkaufsautomaten. Im Inneren befindet sich ein Raspberry Pi und etwas Verdrahtung auf einer separaten Platine. Angeschlossen sind ein M\u00fcnz- und Geldscheinakzeptor, ein Bankterminal\u2026 Alles wird von einem selbstgeschriebenen Programm gesteuert. Die gesamte Arbeitsgeschichte wird auf einem Protokoll auf einem Flash-Laufwerk (MicroSD) aufgezeichnet, das dann \u00fcber das Internet (mit einem USB-Modem) an einen Server gesendet wird, wo es in einer Datenbank gespeichert wird. Verkaufsinformationen werden in 1C hochgeladen, au\u00dferdem gibt es","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","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. \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","og:url":"https:\/\/prohoster.info\/de\/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"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/53751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=53751"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/53751\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=53751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=53751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=53751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}