{"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\/pl\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","title":{"rendered":"Moja implementacja bufora ringowego w pami\u0119ci NOR flash","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<h1 id=\"predystoriya\">T\u0142o<\/h1>\n<p><\/p>\n<p>Mamy automaty sprzeda\u017cowe, kt\u00f3re sami opracowali\u015bmy. Wewn\u0105trz znajduje si\u0119 Raspberry Pi oraz nieco dodatkowego okablowania na osobnej p\u0142ycie. Pod\u0142\u0105czone s\u0105 do nich monety, banknoty, terminal p\u0142atniczy\u2026 Ca\u0142o\u015bci\u0105 zarz\u0105dza autorski program. Ca\u0142a historia dzia\u0142ania zapisywana jest w dzienniku na pami\u0119ci flash (MicroSD), kt\u00f3ry nast\u0119pnie przesy\u0142any jest przez internet (za pomoc\u0105 modemu USB) na serwer, gdzie przechowywana jest w bazie danych. Informacje o sprzeda\u017cy s\u0105 za\u0142adowane do 1C, mamy r\u00f3wnie\u017c prosty interfejs webowy do monitorowania itp. <\/p>\n<p><\/p>\n<p>Zatem dziennik jest niezwykle wa\u017cny \u2014 do ewidencji (s\u0105 tam przychody, sprzeda\u017c itd.), monitorowania (wszelkie awarie i inne sytuacje nadzwyczajne); mo\u017cna powiedzie\u0107, \u017ce to ca\u0142a informacja, jak\u0105 posiadamy o tym automacie. <\/p>\n<p><\/p>\n<h1 id=\"problema\">Problem<\/h1>\n<p><\/p>\n<p>Pami\u0119ci USB okazuj\u0105 si\u0119 bardzo zawodnymi urz\u0105dzeniami. Regularnie si\u0119 psuj\u0105. Prowadzi to zar\u00f3wno do przestoj\u00f3w automat\u00f3w, jak i (je\u015bli z jakiego\u015b powodu dziennik nie m\u00f3g\u0142 by\u0107 przes\u0142any online) do utraty danych.<\/p>\n<p><\/p>\n<p><em>To ju\u017c nie pierwsze do\u015bwiadczenie z pami\u0119ciami USB, przedtem by\u0142 inny projekt z ponad setk\u0105 urz\u0105dze\u0144, gdzie dziennik by\u0142 przechowywany na pami\u0119ciach USB, r\u00f3wnie\u017c tam by\u0142y problemy z niezawodno\u015bci\u0105, czasami liczba uszkodzonych w ci\u0105gu miesi\u0105ca wynosi\u0142a dziesi\u0105tki. Pr\u00f3bowali\u015bmy r\u00f3\u017cnych pami\u0119ci, w tym markowych na pami\u0119ci SLC, tak, niekt\u00f3re modele s\u0105 bardziej niezawodne od innych, ale wymiana pami\u0119ci nie rozwi\u0105za\u0142a problemu w spos\u00f3b drastyczny.<\/em><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex> <\/p>\n<p><strong>Uwaga!<\/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\">na koniec<\/a><\/noindex> artyku\u0142em.<\/p>\n<p><\/p>\n<h1 id=\"reshenie\">Rozwi\u0105zanie<\/h1>\n<p><\/p>\n<p>Pierwsza my\u015bl, kt\u00f3ra przychodzi do g\u0142owy: zrezygnowa\u0107 z MicroSD, zainstalowa\u0107 np. SSD i \u0142adowa\u0107 z niego. Teoretycznie to mo\u017cliwe, ale wzgl\u0119dnie drogie i wcale nie tak niezawodne (dodaje si\u0119 adapter USB-SATA; w bud\u017cetowych SSD statystyki awarii te\u017c nie napawaj\u0105 optymizmem).<\/p>\n<p><\/p>\n<p>USB HDD r\u00f3wnie\u017c nie wygl\u0105da na szczeg\u00f3lnie atrakcyjne rozwi\u0105zanie.<\/p>\n<p><\/p>\n<p>Dlatego doszli\u015bmy do takiego rozwi\u0105zania: pozostawi\u0107 uruchamianie z MicroSD, ale u\u017cywa\u0107 ich w trybie tylko do odczytu, a dziennik dzia\u0142ania (i inne unikalne dane dla konkretnego urz\u0105dzenia \u2014 numer seryjny, kalibracje czujnik\u00f3w, itd.) przechowywa\u0107 gdzie indziej. <\/p>\n<p><\/p>\n<p>Temat systemu plik\u00f3w tylko do odczytu dla Raspberry Pi zosta\u0142 ju\u017c dok\u0142adnie zbadany, nie b\u0119d\u0119 si\u0119 zatrzymywa\u0142 na szczeg\u00f3\u0142ach realizacji w tym artykule <em>(ale je\u015bli b\u0119dzie zainteresowanie \u2014 by\u0107 mo\u017ce napisz\u0119 na ten temat mini-artykul)<\/em>. Jedna rzecz, kt\u00f3r\u0105 warto zaznaczy\u0107: zar\u00f3wno z do\u015bwiadczenia osobistego, jak i z opinii os\u00f3b, kt\u00f3re ju\u017c wdro\u017cy\u0142y, mo\u017cna zauwa\u017cy\u0107 popraw\u0119 w niezawodno\u015bci. Tak, ca\u0142kowite wyeliminowanie awarii jest niemo\u017cliwe, ale znaczne zmniejszenie ich cz\u0119stotliwo\u015bci jest ca\u0142kiem realne. Karty staj\u0105 si\u0119 r\u00f3wnie\u017c ujednolicone, co znacznie u\u0142atwia ich wymian\u0119 dla personelu obs\u0142uguj\u0105cego.<\/p>\n<p><\/p>\n<h2 id=\"apparatnaya-chast\">Cz\u0119\u015b\u0107 sprz\u0119towa<\/h2>\n<p><\/p>\n<p>Nie by\u0142o wi\u0119kszych w\u0105tpliwo\u015bci co do wyboru typu pami\u0119ci \u2014 NOR Flash.<br \/>\nArgumenty: <\/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>niska cena;<\/li>\n<li>standardowy protok\u00f3\u0142 pracy (realizacja ju\u017c istnieje w j\u0105drze Linuxa, w razie potrzeby mo\u017cna skorzysta\u0107 z zewn\u0119trznych, kt\u00f3re te\u017c s\u0105 dost\u0119pne, lub nawet napisa\u0107 w\u0142asne, co jest do\u015b\u0107 proste);<\/li>\n<li>niezawodno\u015b\u0107 i trwa\u0142o\u015b\u0107:<br \/>\nz typowego arkusza danych: dane s\u0105 przechowywane przez 20 lat, 100000 cykli kasowania dla ka\u017cdego bloku;<br \/>\nz zewn\u0119trznych \u017ar\u00f3de\u0142: bardzo niski BER, postulowane jest brak konieczno\u015bci stosowania kod\u00f3w korekcji b\u0142\u0119d\u00f3w. <em>(w niekt\u00f3rych pracach omawia si\u0119 ECC dla NOR, ale zazwyczaj ma si\u0119 na my\u015bli MLC NOR, co si\u0119 zdarza)<\/em>.<\/li>\n<\/ul>\n<p><\/p>\n<p>Oszacujmy wymagania dotycz\u0105ce obj\u0119to\u015bci i trwa\u0142o\u015bci.<\/p>\n<p><\/p>\n<p>Chcemy, aby dane by\u0142y gwarantowane do przechowywania przez kilka dni. Potrzebne jest to, aby w przypadku jakichkolwiek problem\u00f3w z komunikacj\u0105 historia sprzeda\u017cy nie zosta\u0142a utracona. B\u0119dziemy bazowa\u0107 na 5 dniach, w tym czasie <em>(nawet przy uwzgl\u0119dnieniu weekend\u00f3w i \u015bwi\u0105t)<\/em> mo\u017cna rozwi\u0105za\u0107 problem.<\/p>\n<p><\/p>\n<p>Obecnie zbieramy oko\u0142o 100 KB dziennie w dzienniku (3-4 tysi\u0105ce wpis\u00f3w), ale ta liczba stopniowo ro\u015bnie \u2014 zwi\u0119ksza si\u0119 szczeg\u00f3\u0142owo\u015b\u0107, dodawane s\u0105 nowe wydarzenia. Poza tym czasami zdarzaj\u0105 si\u0119 wzrosty (jaki\u015b czujnik zaczyna generowa\u0107 fa\u0142szywe alarmy, na przyk\u0142ad). Planujemy na 10 tysi\u0119cy wpis\u00f3w po 100 bajt\u00f3w \u2014 megabajt na dzie\u0144.<\/p>\n<p><\/p>\n<p>W sumie wychodzi 5 MB ''czystych'' (dobrze kompresowalnych) danych. Do nich jeszcze <em>(przybli\u017cone oszacowanie)<\/em> 1 MB danych systemowych.<\/p>\n<p><\/p>\n<p>To znaczy, \u017ce potrzebujemy uk\u0142adu o pojemno\u015bci 8 MB, je\u015bli nie u\u017cywamy kompresji, lub 4 MB, je\u015bli u\u017cywamy. To ca\u0142kiem realne liczby dla tego typu pami\u0119ci.<\/p>\n<p><\/p>\n<p>Je\u015bli chodzi o trwa\u0142o\u015b\u0107: je\u015bli zak\u0142adamy, \u017ce pami\u0119\u0107 b\u0119dzie zapisywana w ca\u0142o\u015bci nie cz\u0119\u015bciej ni\u017c raz na 5 dni, to przez 10 lat u\u017cytkowania otrzymujemy mniej ni\u017c tysi\u0105c cykli zapisu.<br \/>\nPrzypominam, \u017ce producent obiecuje sto tysi\u0119cy.<\/p>\n<p>\n<b class=\"spoiler_title\">Troch\u0119 o NOR vs NAND<\/b><\/p>\n<p>Dzisiaj zdecydowanie bardziej popularna jest pami\u0119\u0107 NAND, ale dla tego projektu nie wykorzysta\u0142bym jej: NAND, w przeciwie\u0144stwie do NOR, wymaga stosowania kod\u00f3w korekcji b\u0142\u0119d\u00f3w, tabeli uszkodzonych blok\u00f3w itd., a n\u00f3\u017cek w uk\u0142adach NAND zazwyczaj jest znacznie wi\u0119cej.<\/p>\n<p><\/p>\n<p>Jako wady NOR mo\u017cna wskaza\u0107:<\/p>\n<p><\/p>\n<ul>\n<li>ma\u0142\u0105 pojemno\u015b\u0107 (a co za tym idzie, wysok\u0105 cen\u0119 za megabajt);<\/li>\n<li>nisk\u0105 pr\u0119dko\u015b\u0107 wymiany (w du\u017cej mierze z powodu u\u017cywania interfejsu szeregowego, zazwyczaj SPI lub I2C);<\/li>\n<li>wolne kasowanie (w zale\u017cno\u015bci od rozmiaru bloku, trwa od u\u0142amk\u00f3w sekundy do kilku sekund).<\/li>\n<\/ul>\n<p><\/p>\n<p>Wydaje si\u0119, \u017ce nie ma nic krytycznego dla nas, wi\u0119c kontynuujemy.<\/p>\n<p><\/p>\n<p>Je\u015bli interesuj\u0105 szczeg\u00f3\u0142y, wybrano uk\u0142ad <noindex><a rel=\"nofollow\" href=\"https:\/\/www.adestotech.com\/wp-content\/uploads\/doc3686.pdf\">at25df321a<\/a><\/noindex> <em>(zreszt\u0105, to nieistotne, na rynku jest mn\u00f3stwo analog\u00f3w, zgodnych pod wzgl\u0119dem pin\u00f3w i systemu komend; nawet je\u015bli zechcemy u\u017cy\u0107 uk\u0142adu innego producenta i\/lub innej pojemno\u015bci, wszystko zadzia\u0142a bez zmiany kodu)<\/em>.<\/p>\n<p><\/p>\n<p>U\u017cywam wbudowanego w j\u0105dro Linux sterownika, na Raspberry dzi\u0119ki wsparciu device tree overlay wszystko jest bardzo proste \u2014 wystarczy umie\u015bci\u0107 skompilowany overlay w \/boot\/overlays i nieco zmodyfikowa\u0107 \/boot\/config.txt.<\/p>\n<p>\n<b class=\"spoiler_title\">Przyk\u0142ad pliku dts<\/b><\/p>\n<p>Szczerze m\u00f3wi\u0105c, nie jestem pewien, czy jest napisany bez b\u0142\u0119d\u00f3w, ale dzia\u0142a.<\/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\">I jeszcze jedna linia w config.txt<\/b><\/p>\n<pre><code class=\"plaintext\">dtoverlay=at25:spimaxfrequency=50000000<\/code><\/pre>\n<p><\/p>\n<p>Opis samego po\u0142\u0105czenia uk\u0142adu z Raspberry Pi pomijam. Z jednej strony nie jestem specjalist\u0105 w elektronice, z drugiej \u2014 tu wszystko jest banalne nawet dla mnie: uk\u0142ad ma tylko 8 n\u00f3g, z kt\u00f3rych potrzebujemy ziemi, zasilania, SPI (CS, SI, SO, SCK); poziomy odpowiadaj\u0105 poziomom Raspberry Pi, \u017cadnych dodatkowych element\u00f3w nie trzeba \u2014 wystarczy po\u0142\u0105czy\u0107 wskazane 6 styk\u00f3w.<\/p>\n<p><\/p>\n<h2 id=\"postanovka-zadachi\">Sformu\u0142owanie zadania<\/h2>\n<p><\/p>\n<p>Jak zawsze, postawienie zadania odbywa si\u0119 w kilku iteracjach, wydaje mi si\u0119, \u017ce nadszed\u0142 czas na kolejn\u0105. wi\u0119c zatrzymajmy si\u0119, zbierzmy to, co zosta\u0142o ju\u017c napisane, i wyja\u015bnijmy pozosta\u0142e niejasne szczeg\u00f3\u0142y.<\/p>\n<p><\/p>\n<p>Zatem ustalili\u015bmy, \u017ce dziennik b\u0119dzie przechowywany w SPI NOR Flash.<\/p>\n<p>\n<b class=\"spoiler_title\">Czym jest NOR Flash dla tych, kt\u00f3rzy nie wiedz\u0105<\/b><\/p>\n<p>To pami\u0119\u0107 nieulotna, z kt\u00f3r\u0105 mo\u017cna wykona\u0107 trzy operacje:<\/p>\n<p><\/p>\n<ol>\n<li>Odczyt:<br \/>\nNajzwyklejsze odczytanie: podajemy adres i odczytujemy tyle bajt\u00f3w, ile potrzebujemy;<\/li>\n<li>Zapis:<br \/>\nZapis do pami\u0119ci NOR flash wygl\u0105da jak zwyk\u0142y, ale ma jedn\u0105 szczeg\u00f3ln\u0105 cech\u0119: mo\u017cna tylko zmienia\u0107 1 na 0, ale nie odwrotnie. Na przyk\u0142ad, je\u015bli w kom\u00f3rce pami\u0119ci mieli\u015bmy 0x55, to po zapisaniu 0x0f, b\u0119dzie tam przechowywana 0x05. <em>(patrz tabela nieco ni\u017cej)<\/em>;<\/li>\n<li>Wymazywanie:<br \/>\nOczywi\u015bcie, musimy umie\u0107 wykonywa\u0107 r\u00f3wnie\u017c operacj\u0119 odwrotn\u0105 \u2014 zmienia\u0107 0 na 1, dok\u0142adnie w tym celu istnieje operacja wymazywania. W przeciwie\u0144stwie do pierwszych dw\u00f3ch, operuje ona nie na bajtach, a na blokach (minimalny blok wymazywania w wybranej pami\u0119ci to 4kB). Operacja wymazywania niszczy ca\u0142y blok i to jest jedyny spos\u00f3b, aby zmieni\u0107 0 na 1. Dlatego podczas pracy z pami\u0119ci\u0105 flash cz\u0119sto konieczne jest wyr\u00f3wnanie struktur danych do granicy bloku wymazywania.<br \/>\nZapis do pami\u0119ci NOR Flash:<\/li>\n<\/ol>\n<p><\/p>\n<p>Dane binarne<\/p>\n<p><strong>By\u0142o<\/strong><br \/>\n<code>01010101<\/code><\/p>\n<p><strong>Zapisano<\/strong><br \/>\n<code>00001111<\/code><\/p>\n<p><strong>Sta\u0142o si\u0119<\/strong><br \/>\n<code>00000101<\/code><\/p>\n<p><\/p>\n<p>Sam dziennik stanowi sekwencj\u0119 rekord\u00f3w o zmiennej d\u0142ugo\u015bci. Typowa d\u0142ugo\u015b\u0107 rekordu wynosi oko\u0142o 30 bajt\u00f3w (cho\u0107 czasami zdarzaj\u0105 si\u0119 te\u017c rekordy o d\u0142ugo\u015bci kilku kilobajt\u00f3w). <em>W tym przypadku pracujemy z nimi po prostu jako z zestawem bajt\u00f3w, ale, je\u015bli to interesuje, wewn\u0105trz rekord\u00f3w stosuje si\u0119 CBOR.<\/em><\/p>\n<p><\/p>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u0436\u0443\u0440\u043d\u0430\u043b\u0430, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u0443\u044e &#171;\u043d\u0430\u0441\u0442\u0440\u043e\u0435\u0447\u043d\u0443\u044e&#187; \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u043a\u0430\u043a \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c\u0443\u044e, \u0442\u0430\u043a \u0438 \u043d\u0435\u0442: \u043d\u0435\u043a\u0438\u0439 ID \u0430\u043f\u043f\u0430\u0440\u0430\u0442\u0430, \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0438 \u0434\u0430\u0442\u0447\u0438\u043a\u043e\u0432, \u0444\u043b\u0430\u0433 &#171;\u0430\u043f\u043f\u0430\u0440\u0430\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d&#187;, etc.<br \/>\nTe informacje stanowi\u0105 zestaw rekord\u00f3w klucz-warto\u015b\u0107, r\u00f3wnie\u017c przechowywanych w CBOR. Mamy ich niewiele (maksymalnie kilka kilobajt\u00f3w), aktualizuj\u0105 si\u0119 one rzadko.<br \/>\nW dalszej cz\u0119\u015bci b\u0119dziemy nazywa\u0107 je kontekstem.<\/p>\n<p><\/p>\n<p>Je\u015bli przypomnimy sobie, od czego zacz\u0119\u0142a si\u0119 ten artyku\u0142, bardzo wa\u017cne jest zapewnienie niezawodno\u015bci przechowywania danych i, gdy to mo\u017cliwe, nieprzerwanej pracy, nawet w przypadku awarii sprz\u0119towych\/uszkodzenia danych.<\/p>\n<p><\/p>\n<p>Jakie \u017ar\u00f3d\u0142a problem\u00f3w mo\u017cna rozwa\u017cy\u0107?<\/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 \/>\nInformacje z <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\">dyskusji<\/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>B\u0142\u0119dy samej pami\u0119ci flash.<br \/>\nBER, cho\u0107 bardzo niski, nie mo\u017ce by\u0107 r\u00f3wny zeru;<\/li>\n<li>B\u0142\u0119dy na szynie<br \/>\nDane przesy\u0142ane przez SPI nie s\u0105 w \u017caden spos\u00f3b zabezpieczone, mog\u0105 wyst\u0105pi\u0107 zar\u00f3wno pojedyncze b\u0142\u0119dy bitowe, jak i b\u0142\u0119dy synchronizacji - utrata lub wstawianie bit\u00f3w (co prowadzi do masowych zniekszta\u0142ce\u0144 danych);<\/li>\n<li>Inne b\u0142\u0119dy\/usterki<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>Sformu\u0142owa\u0142em wymagania, kt\u00f3rych spe\u0142nienie, moim zdaniem, jest niezb\u0119dne dla zapewnienia niezawodno\u015bci:<\/p>\n<p><\/p>\n<ul>\n<li>zapisy musz\u0105 trafia\u0107 do pami\u0119ci flash natychmiast, zapis op\u00f3\u017aniony nie jest brany pod uwag\u0119; - je\u015bli wyst\u0105pi b\u0142\u0105d, powinien by\u0107 wykrywany i przetwarzany jak najszybciej; - system powinien, je\u015bli to mo\u017cliwe, przywraca\u0107 dzia\u0142anie po b\u0142\u0119dach.<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\">Pomys\u0142y, podej\u015bcia, refleksje<\/h2>\n<p><\/p>\n<p>Gdy zacz\u0105\u0142em my\u015ble\u0107 nad tym zadaniem, w mojej g\u0142owie przewija\u0142o si\u0119 mn\u00f3stwo pomys\u0142\u00f3w, na przyk\u0142ad:<\/p>\n<p><\/p>\n<ul>\n<li>u\u017cycie kompresji danych;<\/li>\n<li>u\u017cycie sprytnych struktur danych, na przyk\u0142ad przechowywanie nag\u0142\u00f3wk\u00f3w zapis\u00f3w oddzielnie od samych zapis\u00f3w, aby w przypadku b\u0142\u0119du w jakim\u015b zapisie mo\u017cna by\u0142o bez problemu odczyta\u0107 pozosta\u0142e;<\/li>\n<li>u\u017cycie p\u00f3l bitowych do kontroli zako\u0144czenia zapisu przy wy\u0142\u0105czeniu zasilania;<\/li>\n<li>przechowywanie sum kontrolnych dla wszystkiego i wsz\u0119dzie;<\/li>\n<li>u\u017cycie jakiego\u015b rodzaju kodowania odpornego na zak\u0142\u00f3cenia.<\/li>\n<\/ul>\n<p><\/p>\n<p>Cz\u0119\u015b\u0107 tych pomys\u0142\u00f3w zosta\u0142a wykorzystana, cz\u0119\u015b\u0107 postanowiono odrzuci\u0107. Zr\u00f3bmy to po kolei.<\/p>\n<p><\/p>\n<h3 id=\"szhatie-dannyh\">Kompresja danych<\/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>Koszty zwi\u0105zane z kompresj\u0105 s\u0105 nieistotne (nasz procesor jest wystarczaj\u0105co mocny, nawet w pierwszym Pi by\u0142o jedno rdze\u0144 o cz\u0119stotliwo\u015bci 700 MHz, w aktualnych modelach kilka rdzeni o cz\u0119stotliwo\u015bci powy\u017cej gigaherca), pr\u0119dko\u015b\u0107 wymiany z pami\u0119ci\u0105 jest niska (kilka megabajt\u00f3w na sekund\u0119), a rozmiar zapis\u00f3w jest niewielki. Og\u00f3lnie rzecz bior\u0105c, je\u015bli kompresja b\u0119dzie mia\u0142a wp\u0142yw na wydajno\u015b\u0107, to tylko pozytywny <em>(absolutnie niekrytyczne, po prostu stwierdzam)<\/em>. Mamy przecie\u017c zwyk\u0142y system Linux, a nie prawdziwie wbudowany, wi\u0119c realizacja nie powinna wymaga\u0107 du\u017cego wysi\u0142ku (wystarczy po\u0142\u0105czy\u0107 bibliotek\u0119 i u\u017cy\u0107 kilku jej funkcji).<\/p>\n<p><\/p>\n<p>Wzi\u0119to fragment logu z dzia\u0142aj\u0105cego urz\u0105dzenia (1,7 MB, 70 tysi\u0119cy wpis\u00f3w) i na pocz\u0105tku sprawdzono jego kompresj\u0119 przy pomocy dost\u0119pnych na komputerze gzip, lz4, lzop, bzip2, xz, zstd.<\/p>\n<p><\/p>\n<ul>\n<li>gzip, xz, zstd pokaza\u0142y podobne wyniki (40 KB).<br \/>\nZdziwi\u0142o mnie, \u017ce modny xz wypad\u0142 na poziomie gzip lub zstd;<\/li>\n<li>lzip z ustawieniami fabrycznymi da\u0142 nieco gorszy wynik;<\/li>\n<li>lz4 i lzop pokaza\u0142y niezbyt dobre wyniki (150 KB);<\/li>\n<li>bzip2 pokaza\u0142 zaskakuj\u0105co dobry wynik (18 KB).<\/li>\n<\/ul>\n<p><\/p>\n<p>Tak wi\u0119c dane kompresuj\u0105 si\u0119 bardzo dobrze.<br \/>\nWi\u0119c (je\u015bli nie znajdziemy powa\u017cnych wad) kompresja b\u0119dzie! Po prostu dlatego, \u017ce na t\u0119 sam\u0105 pami\u0119\u0107 USB zmie\u015bci si\u0119 wi\u0119cej danych.<\/p>\n<p><\/p>\n<p>Zastan\u00f3wmy si\u0119 nad wadami.<\/p>\n<p><\/p>\n<p>Pierwszy problem: ju\u017c ustalili\u015bmy, \u017ce ka\u017cdy wpis musi natychmiast trafi\u0107 na pami\u0119\u0107 USB. Zwykle kompresor gromadzi dane z strumienia wej\u015bciowego, a\u017c zdecyduje, \u017ce czas pisa\u0107 do wyj\u015bcia. Nam z kolei potrzebny jest natychmiast skompresowany blok danych, kt\u00f3ry musimy zapisa\u0107 w pami\u0119ci nieulotnej.<\/p>\n<p><\/p>\n<p>Widz\u0119 trzy \u015bcie\u017cki:<\/p>\n<p><\/p>\n<ol>\n<li>Komprimir ka\u017cde wpis za pomoc\u0105 kompresji s\u0142ownikowej zamiast wcze\u015bniej om\u00f3wionych algorytm\u00f3w.<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 \/>\nRozwa\u017cane algorytmy kompresji nie s\u0105 zaprojektowane do pracy z wpisami takiej wielko\u015bci (rz\u0119du dziesi\u0105tek bajt\u00f3w), wsp\u00f3\u0142czynnik kompresji b\u0119dzie wyra\u017anie mniejszy ni\u017c 1 (to znaczy zwi\u0119kszenie obj\u0119to\u015bci danych zamiast ich kompresji);<\/li>\n<li>Dokonywa\u0107 FLUSH po ka\u017cdej wpisie.<br \/>\nW wielu bibliotekach kompresji jest obs\u0142uga FLUSH. Jest to polecenie (lub parametr dla procesu kompresji), kt\u00f3re po otrzymaniu sprawia, \u017ce kompresor tworzy skompresowany strumie\u0144 w taki spos\u00f3b, aby na jego podstawie mo\u017cna by\u0142o odzyska\u0107 <strong>wszystkie<\/strong> rozpakowane dane, kt\u00f3re zosta\u0142y ju\u017c uzyskane. Taki odpowiednik <code>sync<\/code> w systemach plikowych lub <code>commit<\/code> w sql.<br \/>\nWa\u017cne jest, \u017ce nast\u0119pne operacje kompresji b\u0119d\u0105 mog\u0142y wykorzysta\u0107 zgromadzony s\u0142ownik, a stopie\u0144 kompresji nie ucierpi tak bardzo jak w poprzedniej wersji.<\/li>\n<\/ol>\n<p><\/p>\n<p>My\u015bl\u0119, \u017ce oczywiste jest, \u017ce wybra\u0142em trzeci\u0105 opcj\u0119, zatrzymamy si\u0119 na niej dok\u0142adniej.<\/p>\n<p><\/p>\n<p>Znaleziono <noindex><a rel=\"nofollow\" href=\"https:\/\/www.bolet.org\/~pornin\/deflate-flush.html\">\u015bwietny artyku\u0142<\/a><\/noindex> o FLUSH w zlib.<\/p>\n<p><\/p>\n<p>Wykona\u0142em test w oparciu o artyku\u0142, wzi\u0119\u0142em 70 tysi\u0119cy wpis\u00f3w dziennika z rzeczywistego urz\u0105dzenia, przy rozmiarze strony wynosz\u0105cym 60KB <em>(do rozmiaru strony wr\u00f3cimy jeszcze)<\/em> otrzyma\u0142em:<\/p>\n<p><\/p>\n<p>Dane \u017ar\u00f3d\u0142owe<br \/>\nKompresja gzip -9 (bez FLUSH)<br \/>\nzlib z Z_PARTIAL_FLUSH<br \/>\nzlib z Z_SYNC_FLUSH<\/p>\n<p><strong>Obj\u0119to\u015b\u0107, KB<\/strong><br \/>\n1692<br \/>\n40<br \/>\n352<br \/>\n604<\/p>\n<p><\/p>\n<p>Na pierwszy rzut oka koszt zwi\u0105zany z FLUSH wydaje si\u0119 niezwykle wysoki, jednak w rzeczywisto\u015bci mamy niewielki wyb\u00f3r \u2014 albo wcale nie kompresowa\u0107, albo kompresowa\u0107 (i to bardzo efektywnie) z FLUSH. Nie nale\u017cy zapomina\u0107, \u017ce mamy 70 tysi\u0119cy wpis\u00f3w, a nadmiar wprowadzany przez Z_PARTIAL_FLUSH wynosi zaledwie 4-5 bajt\u00f3w na wpis. A wsp\u00f3\u0142czynnik kompresji wyni\u00f3s\u0142 prawie 5:1, co jest wi\u0119cej ni\u017c doskona\u0142ym wynikiem.<\/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>W przypadku u\u017cycia Z_SYNC_FLUSH 4 ostatnie bajty ka\u017cdego wpisu b\u0119d\u0105 zawsze 0x00, 0x00, 0xff, 0xff. A je\u015bli je znamy \u2014 mo\u017cemy ich nie przechowywa\u0107, w ten spos\u00f3b ko\u0144cowy rozmiar wynosi tylko 324KB.<\/p>\n<p><\/p>\n<p>W artykule, do kt\u00f3rego si\u0119 odwo\u0142uj\u0119, jest wyja\u015bnienie:<\/p>\n<p><\/p>\n<blockquote><p>Nowy blok typu 0 z pust\u0105 zawarto\u015bci\u0105 jest do\u0142\u0105czany.<\/p>\n<p>Blok typu 0 z pust\u0105 zawarto\u015bci\u0105 sk\u0142ada si\u0119 z:<\/p>\n<ul>\n<li>nag\u0142\u00f3wka bloku o d\u0142ugo\u015bci trzech bit\u00f3w;<\/li>\n<li>0 do 7 bit\u00f3w r\u00f3wnych zeru, aby osi\u0105gn\u0105\u0107 wyr\u00f3wnanie bajtowe;<\/li>\n<li>czterobajtowej sekwencji 00 00 FF FF.<\/li>\n<\/ul>\n<p>\n<\/p><\/blockquote>\n<p>Jak \u0142atwo zauwa\u017cy\u0107, przed tymi 4 bajtami w ostatnim bloku wyst\u0119puje od 3 do 10 zerowych bit\u00f3w. Jednak praktyka pokaza\u0142a, \u017ce rzeczywi\u015bcie zera to przynajmniej 10.<\/p>\n<p><\/p>\n<p>Okazuje si\u0119, \u017ce tak kr\u00f3tkie bloki danych s\u0105 zazwyczaj (zawsze?) kodowane za pomoc\u0105 bloku typu 1 (blok sta\u0142y), kt\u00f3ry zawsze ko\u0144czy si\u0119 7 zerowymi bitami, co daje \u0142\u0105cznie 10-17 gwarantowanych zerowych bit\u00f3w (a pozosta\u0142e b\u0119d\u0105 zerowe z prawdopodobie\u0144stwem oko\u0142o 50%).<\/p>\n<p><\/p>\n<p>Tak wi\u0119c, w danych testowych w 100% przypadk\u00f3w przed 0x00, 0x00, 0xff, 0xff wyst\u0119puje jeden zerowy bajt, a w wi\u0119cej ni\u017c jednej trzeciej przypadk\u00f3w \u2014 dwa zerowe bajty <em>(mo\u017cliwe, \u017ce wynika to z faktu, \u017ce u\u017cywam binarnego CBOR, a przy u\u017cyciu tekstowego JSON cz\u0119\u015bciej wyst\u0119powa\u0142yby bloki typu 2 \u2014 blok dynamiczny, a zatem wyst\u0119powa\u0142yby bloki bez dodatkowych zerowych bajt\u00f3w przed 0x00, 0x00, 0xff, 0xff)<\/em>.<\/p>\n<p><\/p>\n<p>W sumie w dost\u0119pnych danych testowych mo\u017cna zmie\u015bci\u0107 si\u0119 w mniej ni\u017c 250KB skompresowanych danych.<\/p>\n<p><\/p>\n<p>Mo\u017cna jeszcze troch\u0119 zaoszcz\u0119dzi\u0107, zajmuj\u0105c si\u0119 \u017conglowaniem bitami: obecnie ignorujemy obecno\u015b\u0107 kilku zerowych bit\u00f3w na ko\u0144cu bloku, kilka bit\u00f3w na pocz\u0105tku bloku r\u00f3wnie\u017c si\u0119 nie zmienia...<br \/>\nAle w tym momencie podj\u0105\u0142em stanowcz\u0105 decyzj\u0119, aby si\u0119 zatrzyma\u0107, bo w takim tempie mo\u017cna doj\u015b\u0107 do stworzenia w\u0142asnego archiwizatora.<\/p>\n<p><\/p>\n<p>W rezultacie, z moich danych testowych uzyska\u0142em 3-4 bajty na zapis, wsp\u00f3\u0142czynnik kompresji wyni\u00f3s\u0142 ponad 6:1. Szczerze m\u00f3wi\u0105c, nie spodziewa\u0142em si\u0119 takiego wyniku, moim zdaniem wszystko, co lepsze od 2:1 to ju\u017c wynik, kt\u00f3ry uzasadnia u\u017cycie kompresji.<\/p>\n<p><\/p>\n<p>Wszystko \u015bwietnie, ale zlib (deflate) to jednak archaiczny, zas\u0142u\u017cony i troch\u0119 staromodny algorytm kompresji. Ju\u017c sama kwestia, \u017ce jako s\u0142ownik wykorzystuje ostatnie 32 KB ze strumienia niekompresowanych danych, dzisiaj wydaje si\u0119 dziwna (to znaczy, je\u015bli jaki\u015b blok danych bardzo przypomina to, co by\u0142o w strumieniu wej\u015bciowym 40 KB temu, b\u0119dzie on zaczyna\u0142 si\u0119 archiwizowa\u0107 od nowa, a nie odnosi\u0142 do wcze\u015bniejszego wyst\u0105pienia). W nowoczesnych archiwizatorach rozmiar s\u0142ownika cz\u0119\u015bciej mierzy si\u0119 w megabajtach, a nie w kilobajtach.<\/p>\n<p><\/p>\n<p>Tak wi\u0119c kontynuujemy nasze mini-badanie archiwizator\u00f3w.<\/p>\n<p><\/p>\n<p>Nast\u0119pnym testowanym by\u0142 bzip2 (przypominam, bez FLUSH pokaza\u0142 fantastyczny stopie\u0144 kompresji, prawie 100:1). Niestety, z FLUSH pokaza\u0142 si\u0119 bardzo s\u0142abo, rozmiar skompresowanych danych okaza\u0142 si\u0119 wi\u0119kszy ni\u017c niekompresowanych.<\/p>\n<p>\n<b class=\"spoiler_title\">Moje przypuszczenia co do przyczyn niepowodzenia<\/b><\/p>\n<p>Libbz2 oferuje tylko jedn\u0105 opcj\u0119 flush, kt\u00f3ra, jak si\u0119 wydaje, oczyszcza s\u0142ownik (analogicznie do Z_FULL_FLUSH w zlib), wi\u0119c nie ma mowy o jakiejkolwiek efektywnej kompresji po tym.<\/p>\n<p><\/p>\n<p>A ostatnim testowanym by\u0142 zstd. W zale\u017cno\u015bci od parametr\u00f3w kompresuje albo na poziomie gzip, ale du\u017co szybciej, albo lepiej ni\u017c 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>Ja <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/facebook\/zstd\/issues\/900\">zada\u0142em pytanie<\/a><\/noindex> na stronie projektu w githubie, otrzyma\u0142em odpowied\u017a, \u017ce warto zak\u0142ada\u0107 do 10 bajt\u00f3w danych pomocniczych na ka\u017cdy blok skompresowanych danych, co jest bliskie uzyskanym rezultatom, nie da si\u0119 dogoni\u0107 deflate.<\/p>\n<p><\/p>\n<p>Na tym postanowi\u0142em zako\u0144czy\u0107 eksperymenty z archiwizatorami (przypominam, xz, lzip, lzo, lz4 nie spisa\u0142y si\u0119 jeszcze na etapie test\u00f3w bez FLUSH, a rozwa\u017canie bardziej egzotycznych algorytm\u00f3w kompresji nie mia\u0142o sensu).<\/p>\n<p><\/p>\n<p>Wracamy do problem\u00f3w archiwizacji.<\/p>\n<p><\/p>\n<p>Drugim (jak si\u0119 m\u00f3wi, w porz\u0105dku, a nie wed\u0142ug znaczenia) problemem jest to, \u017ce skompresowane dane s\u0105 jedn\u0105 ci\u0105g\u0142\u0105 strumieniem, w kt\u00f3rym ci\u0105gle wyst\u0119puj\u0105 odniesienia do wcze\u015bniejszych fragment\u00f3w. W ten spos\u00f3b, w przypadku uszkodzenia jakiego\u015b fragmentu skompresowanych danych, tracimy nie tylko powi\u0105zany z nim blok nieskompresowanych danych, ale tak\u017ce wszystkie nast\u0119pne.<\/p>\n<p><\/p>\n<p>Istniej\u0105 podej\u015bcia do rozwi\u0105zania tego problemu:<\/p>\n<p><\/p>\n<ol>\n<li>Zapobiega\u0107 pojawieniu si\u0119 problemu \u2014 dodawa\u0107 do skompresowanych danych nadmiarowo\u015b\u0107, kt\u00f3ra pozwoli na identyfikacj\u0119 i popraw\u0119 b\u0142\u0119d\u00f3w; o tym porozmawiamy p\u00f3\u017aniej;<\/li>\n<li>Minimalizowa\u0107 konsekwencje w przypadku wyst\u0105pienia problemu.<br \/>\nJu\u017c wcze\u015bniej m\u00f3wili\u015bmy, \u017ce mo\u017cemy kompresowa\u0107 ka\u017cdy blok danych niezale\u017cnie, w\u00f3wczas problem zniknie sam w sobie (uszkodzenie danych jednego bloku spowoduje utrat\u0119 danych tylko tego bloku). Jednak to skrajny przypadek, w kt\u00f3rym kompresja danych b\u0119dzie ma\u0142o efektywna. Przeciwie\u0144stwem jest wykorzystanie ca\u0142ych 4 MB naszej pami\u0119ci jako jednego archiwum, co da nam doskona\u0142\u0105 kompresj\u0119, ale katastrofalne konsekwencje w przypadku uszkodzenia danych.<br \/>\n<em>Tak, potrzebny jest kompromis z punktu widzenia niezawodno\u015bci. Nale\u017cy jednak pami\u0119ta\u0107, \u017ce opracowujemy format przechowywania danych dla pami\u0119ci nieulotnej o ekstremalnie niskim BER i deklarowanym okresie przechowywania danych wynosz\u0105cym 20 lat.<\/em><\/li>\n<\/ol>\n<p><\/p>\n<p>W trakcie eksperyment\u00f3w odkry\u0142em, \u017ce bardziej zauwa\u017calne straty w poziomie kompresji zaczynaj\u0105 si\u0119 dla blok\u00f3w skompresowanych danych o rozmiarze poni\u017cej 10 KB.<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>To znaczy, minimalny rozumny rozmiar strony wynosi 16 KB (z zapasem na informacje pomocnicze). Jednak tak ma\u0142y rozmiar strony nak\u0142ada istotne ograniczenia na maksymalny rozmiar wpisu.<\/p>\n<p><\/p>\n<p>Chocia\u017c nie przewiduj\u0119 na razie wpis\u00f3w wi\u0119kszych ni\u017c kilka kilobajt\u00f3w w skompresowanej postaci, zdecydowa\u0142em si\u0119 na u\u017cycie stron o rozmiarze 32 KB (co daje \u0142\u0105cznie 128 stron na uk\u0142adzie).<\/p>\n<p><\/p>\n<p><strong>Podsumowanie:<\/strong><\/p>\n<p><\/p>\n<ul>\n<li>Dane przechowujemy w spos\u00f3b skompresowany z u\u017cyciem zlib (deflate);<\/li>\n<li>Ustawiamy Z_SYNC_FLUSH dla ka\u017cdego wpisu;<\/li>\n<li>Obcinamy ko\u0144cowe bajty ka\u017cdej skompresowanej notatki <em>(np. 0x00, 0x00, 0xff, 0xff)<\/em>; w nag\u0142\u00f3wku podajemy, ile bajt\u00f3w obci\u0119li\u015bmy;<\/li>\n<li>Dane przechowujemy w stronach o wielko\u015bci 32 KB; w obr\u0119bie strony idzie jednolity strumie\u0144 skompresowanych danych; na ka\u017cdej stronie kompresj\u0119 rozpoczynamy od nowa.<\/li>\n<\/ul>\n<p><\/p>\n<p>I zanim zako\u0144czymy temat kompresji, chcia\u0142bym zwr\u00f3ci\u0107 uwag\u0119, \u017ce skompresowanych danych uzyskujemy tylko kilka bajt\u00f3w na zapis, dlatego niezwykle wa\u017cne jest, aby nie zwi\u0119ksza\u0107 danych pomocniczych, ka\u017cdy bajt si\u0119 liczy.<\/p>\n<p><\/p>\n<h3 id=\"hranenie-zagolovkov-dannyh\">Przechowywanie nag\u0142\u00f3wk\u00f3w danych<\/h3>\n<p><\/p>\n<p>Poniewa\u017c mamy do czynienia z zapisami o zmiennej d\u0142ugo\u015bci, musimy w jaki\u015b spos\u00f3b okre\u015bli\u0107 rozmieszczenie\/granice zapis\u00f3w.<\/p>\n<p><\/p>\n<p>Znam trzy podej\u015bcia:<\/p>\n<p><\/p>\n<ol>\n<li>Wszystkie zapisy s\u0105 przechowywane w ci\u0105g\u0142ym strumieniu, najpierw znajduje si\u0119 nag\u0142\u00f3wek zapisu, kt\u00f3ry zawiera d\u0142ugo\u015b\u0107, a nast\u0119pnie sam zapis.<br \/>\nW tej opcji zar\u00f3wno nag\u0142\u00f3wki, jak i dane mog\u0105 mie\u0107 zmienn\u0105 d\u0142ugo\u015b\u0107.<br \/>\nW zasadzie otrzymujemy pojedynczy powi\u0105zany list, kt\u00f3ry jest u\u017cywany wsz\u0119dzie;<\/li>\n<li>Nag\u0142\u00f3wki i same zapisy s\u0105 przechowywane w osobnych strumieniach.<br \/>\nKorzystaj\u0105c z nag\u0142\u00f3wk\u00f3w sta\u0142ej d\u0142ugo\u015bci, osi\u0105gamy to, \u017ce uszkodzenie jednego nag\u0142\u00f3wka nie wp\u0142ywa na pozosta\u0142e.<br \/>\nPodobne podej\u015bcie jest stosowane na przyk\u0142ad w wielu systemach plik\u00f3w;<\/li>\n<li>Zapisy s\u0105 przechowywane w ci\u0105g\u0142ym strumieniu, granice zapisu s\u0105 okre\u015blane na podstawie pewnego znacznika (znaku\/sekwencji znak\u00f3w, kt\u00f3ra jest zabroniona wewn\u0105trz blok\u00f3w danych). Je\u015bli w obr\u0119bie zapisu napotkamy znacznik, zast\u0119pujemy go pewn\u0105 sekwencj\u0105 (escape'ujemy go).<br \/>\nPodobne podej\u015bcie jest stosowane na przyk\u0142ad w protokole PPP.<\/li>\n<\/ol>\n<p><\/p>\n<p>Ilustruj\u0119.<\/p>\n<p><\/p>\n<p>Opcja 1:<br \/>\n<img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/e5a9676ca21eaca07e64ddf9fcd9f2eb.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nTu wszystko jest bardzo proste: znaj\u0105c d\u0142ugo\u015b\u0107 zapisu, mo\u017cemy obliczy\u0107 adres nast\u0119pnego nag\u0142\u00f3wka. Tak poruszamy si\u0119 po nag\u0142\u00f3wkach, a\u017c napotkamy obszar wype\u0142niony 0xff (wolny obszar) lub koniec strony.<\/p>\n<p><\/p>\n<p>Opcja 2:<br \/>\n<img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci 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>Wariant 3:<br \/>\n<img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/dfb64009aa7781fcb8a47423ff4160c6.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nNie ma potrzeby przechowywa\u0107 w nag\u0142\u00f3wku d\u0142ugo\u015bci lub innych informacji o lokalizacji danych; wystarcz\u0105 znaczniki oznaczaj\u0105ce granice wpis\u00f3w. Jednak dane musz\u0105 by\u0107 przetwarzane podczas zapisu\/odczytu.<br \/>\nJako znacznik u\u017cy\u0142bym 0xff (kt\u00f3re wype\u0142nia stron\u0119 po wymazaniu), w ten spos\u00f3b wolna przestrze\u0144 na pewno nie b\u0119dzie traktowana jako dane.<\/p>\n<p><\/p>\n<p>Tabela por\u00f3wnawcza:<\/p>\n<p><\/p>\n<p>Opcja 1<br \/>\nOpcja 2<br \/>\nOpcja 3<\/p>\n<p><strong>Odporno\u015b\u0107 na b\u0142\u0119dy<\/strong><br \/>\n\u2014<br \/>\n+<br \/>\n+<\/p>\n<p><strong>Kompaktowo\u015b\u0107<\/strong><br \/>\n+<br \/>\n\u2014<br \/>\n+<\/p>\n<p><strong>Z\u0142o\u017cono\u015b\u0107 realizacji<\/strong><br \/>\n*<br \/>\n**<br \/>\n**<\/p>\n<p><\/p>\n<p>Wariant 1 ma fatalny b\u0142\u0105d: w przypadku uszkodzenia kt\u00f3rego\u015b z nag\u0142\u00f3wk\u00f3w, ca\u0142a nast\u0119pna sekwencja ulega zniszczeniu. Pozosta\u0142e warianty pozwalaj\u0105 na odzyskanie cz\u0119\u015bci danych nawet przy masowych uszkodzeniach.<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>Kompaktowo\u015b\u0107:<\/p>\n<p><\/p>\n<ul>\n<li>w pierwszym wariancie musimy przechowywa\u0107 w nag\u0142\u00f3wku tylko d\u0142ugo\u015b\u0107; je\u015bli u\u017cyjemy zmiennych o zmiennej d\u0142ugo\u015bci, to w wi\u0119kszo\u015bci przypadk\u00f3w mo\u017cemy si\u0119 ograniczy\u0107 do jednego bajta;<\/li>\n<li>w drugim wariancie musimy przechowywa\u0107 adres pocz\u0105tkowy i d\u0142ugo\u015b\u0107; zapis musi by\u0107 sta\u0142ej d\u0142ugo\u015bci, oceniam to na 4 bajty na zapis (dwa bajty na przesuni\u0119cie i dwa bajty na d\u0142ugo\u015b\u0107);<\/li>\n<li>trzeci wariant potrzebuje tylko jednego symbolu do oznaczenia pocz\u0105tku zapisu, a sama zapis z powodu escaped ro\u015bnie o 1-2%. Og\u00f3lnie ca\u0142kowity parytet z pierwszym wariantem.<\/li>\n<\/ul>\n<p><\/p>\n<p>Pocz\u0105tkowo rozwa\u017ca\u0142em drugi wariant jako g\u0142\u00f3wny (i nawet napisa\u0142em jego realizacj\u0119). Zrezygnowa\u0142em tylko wtedy, gdy ostatecznie zdecydowa\u0142em si\u0119 na kompresj\u0119.<\/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>Co do trzeciego wariantu: przyzna\u0142em mu dwie gwiazdki za z\u0142o\u017cono\u015b\u0107 realizacji, po prostu dlatego, \u017ce nie lubi\u0119 bawi\u0107 si\u0119 w escaped, zmiana d\u0142ugo\u015bci w trakcie itp. Tak, by\u0107 mo\u017ce stronniczo, ale kod musz\u0119 pisa\u0107 ja \u2014 po co zmusza\u0107 si\u0119 do robienia czego\u015b, co si\u0119 nie podoba.<\/p>\n<p><\/p>\n<p><strong>Podsumowanie:<\/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\">Zastosowanie p\u00f3l bitowych do kontrolowania sukcesu operacji zapisu<\/h3>\n<p><\/p>\n<p>Ju\u017c teraz nie pami\u0119tam, gdzie podpatrzy\u0142em ten pomys\u0142, ale wygl\u0105da to mniej wi\u0119cej tak:<br \/>\nDla ka\u017cdego wpisu rezerwujemy kilka bit\u00f3w na przechowywanie flag.<br \/>\n<em>Jak m\u00f3wili\u015bmy wcze\u015bniej, po erase wszystkie bity s\u0105 wype\u0142nione 1, a my mo\u017cemy zmienia\u0107 1 na 0, ale nie odwrotnie.<\/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>Oto jak mo\u017ce wygl\u0105da\u0107 umieszczenie zmiennej d\u0142ugo\u015bci w zapisach flash:<\/p>\n<p><\/p>\n<ol>\n<li>Ustalamy flag\u0119 'rozpocz\u0119cie zapisu d\u0142ugo\u015bci';<\/li>\n<li>Zapisujemy d\u0142ugo\u015b\u0107;<\/li>\n<li>Ustalamy flag\u0119 'rozpocz\u0119cie zapisu danych';<\/li>\n<li>Zapisujemy dane;<\/li>\n<li>Ustalamy flag\u0119 'zapis zako\u0144czony'.<\/li>\n<\/ol>\n<p><\/p>\n<p>Opr\u00f3cz tego b\u0119dziemy mieli flag\u0119 'wyst\u0105pi\u0142 b\u0142\u0105d', co daje nam 4 bity flagowe.<\/p>\n<p><\/p>\n<p>W takim przypadku mamy dwa stabilne stany '1111' \u2014 zapis si\u0119 nie rozpocz\u0105\u0142 i '1000' \u2014 zapis przebieg\u0142 pomy\u015blnie; w przypadku niespodziewanego przerwania procesu zapisu uzyskamy stany po\u015brednie, kt\u00f3re nast\u0119pnie b\u0119dziemy mogli wykry\u0107 i przetworzy\u0107.<\/p>\n<p><\/p>\n<p>Podej\u015bcie jest ciekawe, ale chroni tylko przed niespodziewanym odci\u0119ciem zasilania i podobnymi awariami, co, oczywi\u015bcie, jest wa\u017cne, jednak nie jest to jedyna (a nawet nie g\u0142\u00f3wna) przyczyna mo\u017cliwych awarii.<\/p>\n<p><\/p>\n<p><strong>Podsumowanie:<\/strong> Idziemy dalej w poszukiwaniu dobrego rozwi\u0105zania.<\/p>\n<p><\/p>\n<h3 id=\"kontrolnye-summy\">Sumy kontrolne<\/h3>\n<p><\/p>\n<p>Sumy kontrolne r\u00f3wnie\u017c pozwalaj\u0105 upewni\u0107 si\u0119 (z wystarczaj\u0105cym prawdopodobie\u0144stwem), \u017ce odczytujemy dok\u0142adnie to, co mia\u0142o by\u0107 zapisane. I, w przeciwie\u0144stwie do om\u00f3wionych powy\u017cej p\u00f3l bitowych, dzia\u0142aj\u0105 one zawsze.<\/p>\n<p><\/p>\n<p>Je\u015bli rozwa\u017cymy list\u0119 potencjalnych \u017ar\u00f3de\u0142 problem\u00f3w, o kt\u00f3rych m\u00f3wili\u015bmy wcze\u015bniej, to suma kontrolna jest w stanie rozpozna\u0107 b\u0142\u0105d niezale\u017cnie od jego pochodzenia <em>(z wyj\u0105tkiem mo\u017ce z\u0142owrogich kosmit\u00f3w \u2014 ci mog\u0105 r\u00f3wnie\u017c sfa\u0142szowa\u0107 sum\u0119 kontroln\u0105)<\/em>.<\/p>\n<p><\/p>\n<p>Wi\u0119c je\u015bli naszym celem jest sprawdzenie, \u017ce dane s\u0105 nienaruszone, sumy kontrolne to doskona\u0142y pomys\u0142.<\/p>\n<p><\/p>\n<p>Wyb\u00f3r algorytmu obliczania sumy kontrolnej nie budzi\u0142 w\u0105tpliwo\u015bci \u2014 CRC. Z jednej strony, w\u0142a\u015bciwo\u015bci matematyczne pozwalaj\u0105 na 100% wychwycenie b\u0142\u0119d\u00f3w niekt\u00f3rych typ\u00f3w, z drugiej \u2014 na danych losowych zazwyczaj ten algorytm pokazuje prawdopodobie\u0144stwo kolizji niewiele wi\u0119ksze od teoretycznego limitu. <img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/bf9cca3564db7d9d03d3ce49642d3a71.jpg\" style=\"display:block;margin: 0 auto;\" \/>Niech to nie b\u0119dzie najszybszy algorytm, ani zawsze minimalny pod wzgl\u0119dem liczby kolizji, ale ma on jedn\u0105 bardzo wa\u017cn\u0105 cech\u0119: w testach, kt\u00f3re przeprowadza\u0142em, nie natrafi\u0142em na wzorce, na kt\u00f3rych wyra\u017anie by zawi\u00f3d\u0142. Stabilno\u015b\u0107 to kluczowa cecha w tym przypadku.<\/p>\n<p><\/p>\n<p>Przyk\u0142ad obszernego badania: <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo.html\">cz\u0119\u015b\u0107 1<\/a><\/noindex>, <noindex><a rel=\"nofollow\" href=\"http:\/\/amsoftware.narod.ru\/algo2.html\">Part 2<\/a><\/noindex> <em>(linki do narod.ru, przepraszam)<\/em>.<\/p>\n<p><\/p>\n<p>Jednak wyb\u00f3r sumy kontrolnej nie jest zako\u0144czony, CRC to ca\u0142e rodziny sum kontrolnych. Musimy zdecydowa\u0107 o d\u0142ugo\u015bci, a nast\u0119pnie wybra\u0107 wielomian.<\/p>\n<p><\/p>\n<p>Wyb\u00f3r d\u0142ugo\u015bci sumy kontrolnej nie jest tak prostym pytaniem, jak si\u0119 wydaje na pierwszy rzut oka.<\/p>\n<p><\/p>\n<p>Ilustruj\u0119:<br \/>\nNiech prawdopodobie\u0144stwo b\u0142\u0119du w ka\u017cdym bajcie wynosi <img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/89a9be2edf2a43cc9115eba37fa02fc8.jpg\" style=\"display:block;margin: 0 auto;\" \/> a idealna suma kontrolna, obliczmy \u015bredni\u0105 liczb\u0119 b\u0142\u0119d\u00f3w na milion zapis\u00f3w:<\/p>\n<p><\/p>\n<p>Dane, bajt<br \/>\nSuma kontrolna, bajt<br \/>\nNieodkrytych b\u0142\u0119d\u00f3w<br \/>\nFa\u0142szywych wykry\u0107 b\u0142\u0119d\u00f3w<br \/>\n\u0141\u0105cznie b\u0142\u0119dnych wykry\u0107<\/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>Wydawa\u0142oby si\u0119, \u017ce wszystko jest proste \u2014 wybieraj w zale\u017cno\u015bci od d\u0142ugo\u015bci danych chronionych d\u0142ugo\u015b\u0107 sumy kontrolnej z minimaln\u0105 ilo\u015bci\u0105 b\u0142\u0119dnych wykry\u0107 \u2014 i sprawa jest za\u0142atwiona.<\/p>\n<p><\/p>\n<p>Jednak w przypadku kr\u00f3tkich sum kontrolnych pojawia si\u0119 problem: cho\u0107 dobrze wykrywaj\u0105 pojedyncze b\u0142\u0119dy bitowe, mog\u0105 z do\u015b\u0107 du\u017cym prawdopodobie\u0144stwem uzna\u0107 za prawid\u0142owe ca\u0142kowicie losowe dane. Na Habrze by\u0142 ju\u017c artyku\u0142 opisuj\u0105cy <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/428746\/\">problem w realnym \u017cyciu<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>Dlatego, aby uczyni\u0107 przypadkowe dopasowanie sumy kontrolnej praktycznie niemo\u017cliwym, nale\u017cy u\u017cywa\u0107 sum kontrolnych o d\u0142ugo\u015bci 32 bit\u00f3w i wi\u0119cej <em>(dla d\u0142ugo\u015bci wi\u0119kszych ni\u017c 64 bity zazwyczaj stosuje si\u0119 kryptograficzne funkcje haszuj\u0105ce)<\/em>.<\/p>\n<p><\/p>\n<p>Pomimo \u017ce wcze\u015bniej pisa\u0142em, \u017ce trzeba oszcz\u0119dza\u0107 miejsce za wszelk\u0105 cen\u0119, jednak u\u017cyjemy 32-bitowej sumy kontrolnej (16 bit\u00f3w to za ma\u0142o, prawdopodobie\u0144stwo kolizji jest wi\u0119ksze ni\u017c 0.01%; a 24 bity, jak to si\u0119 m\u00f3wi, ani tu, ani tam).<\/p>\n<p><\/p>\n<p>Tu mo\u017ce pojawi\u0107 si\u0119 sprzeciw: czy naprawd\u0119 oszcz\u0119dzali\u015bmy ka\u017cdy bajt przy wyborze kompresji, aby teraz odda\u0107 4 bajty naraz? Czy nie lepiej by\u0142o nie kompresowa\u0107 i nie dodawa\u0107 sumy kontrolnej? Oczywi\u015bcie, \u017ce nie, brak kompresji <em>nie oznacza<\/em>, \u017ce kontrola integralno\u015bci nie jest nam potrzebna.<\/p>\n<p><\/p>\n<p>Nie b\u0119dziemy wynajdowa\u0107 roweru na wyb\u00f3r wielomianu, we\u017amiemy obecnie popularny CRC-32C.<br \/>\nTen kod wykrywa 6 b\u0142\u0119d\u00f3w bitowych w pakietach do 22 bajt\u00f3w (to zapewne najcz\u0119stszy przypadek dla nas), 4 b\u0142\u0119dy bitowe w pakietach do 655 bajt\u00f3w (te\u017c cz\u0119sty przypadek dla nas), 2 lub jak\u0105kolwiek nieparzyst\u0105 liczb\u0119 b\u0142\u0119d\u00f3w bitowych w pakietach o sensownej d\u0142ugo\u015bci.<\/p>\n<p>\n<b class=\"spoiler_title\">Je\u015bli kogo\u015b interesuj\u0105 szczeg\u00f3\u0142y<\/b><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Cyclic_redundancy_check\">artyku\u0142 w Wikipedii<\/a><\/noindex> na temat CRC.<\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0x8f6e37a0_len.txt\">Parametry kodu crc-32c<\/a><\/noindex> na <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/crc\/notes.html\">na stronie Kupmana<\/a><\/noindex> \u2014 by\u0107 mo\u017ce najwa\u017cniejszego specjalisty w dziedzinie CRC na \u015bwiecie.<\/p>\n<p><\/p>\n<p>W <noindex><a rel=\"nofollow\" href=\"http:\/\/users.ece.cmu.edu\/~koopman\/networks\/dsn02\/dsn02_koopman.pdf\">jego artykule<\/a><\/noindex> jest <noindex><a rel=\"nofollow\" href=\"https:\/\/users.ece.cmu.edu\/~koopman\/crc\/c32\/0xfa567d89_len.txt\">jeszcze jeden interesuj\u0105cy kod<\/a><\/noindex>, zapewniaj\u0105cy nieco lepsze parametry dla istotnych dla nas d\u0142ugo\u015bci pakiet\u00f3w, ale nie uzna\u0142em r\u00f3\u017cnicy za istotn\u0105, a siebie za wystarczaj\u0105co kompetentnego, aby wybra\u0107 niestandardowy kod zamiast standardowego i dobrze przebadanego.<\/p>\n<p><\/p>\n<p>Ponadto, poniewa\u017c mamy dane skompresowane, pojawia si\u0119 pytanie: czy oblicza\u0107 sum\u0119 kontroln\u0105 dla danych skompresowanych, czy nieskompresowanych?<\/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>w ko\u0144cu musimy sprawdzi\u0107 integralno\u015b\u0107 przechowywanych danych \u2014 w\u0142a\u015bnie to bezpo\u015brednio sprawdzamy (przy okazji b\u0119d\u0105 r\u00f3wnie\u017c sprawdzone mo\u017cliwe b\u0142\u0119dy w realizacji kompresji\/dekompresji, uszkodzenia spowodowane uszkodzon\u0105 pami\u0119ci\u0105 itd.);<\/li>\n<li>algorytm deflate w zlib ma wystarczaj\u0105co dojrza\u0142\u0105 realizacj\u0119 i <em>nie powinien<\/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>nie bardzo podoba mi si\u0119 pomys\u0142 przesy\u0142ania dekompressorowi potencjalnie uszkodzonych danych, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.cvedetails.com\/vulnerability-list\/vendor_id-72\/product_id-1820\/GNU-Zlib.html\">kto wie,<\/a><\/noindex>jak on zareaguje.<\/li>\n<\/ul>\n<p><\/p>\n<p>W tym projekcie postanowi\u0142em odst\u0105pi\u0107 od og\u00f3lnie przyj\u0119tej praktyki przechowywania sumy kontrolnej nieskompresowanych danych.<\/p>\n<p><\/p>\n<p><strong>Podsumowanie:<\/strong> u\u017cywamy CRC-32C, sum\u0119 kontroln\u0105 obliczamy z danych w takiej formie, w jakiej s\u0105 zapisywane w pami\u0119ci flash (po kompresji).<\/p>\n<p><\/p>\n<h3 id=\"izbytochnost\">Nadwy\u017cka<\/h3>\n<p><\/p>\n<p>U\u017cycie nadmiarowego kodowania nie eliminuje oczywi\u015bcie utraty danych, jednak mo\u017ce znacz\u0105co (cz\u0119sto o wiele rz\u0119d\u00f3w) zmniejszy\u0107 prawdopodobie\u0144stwo nieodwracalnej utraty danych.<\/p>\n<p><\/p>\n<p>Mo\u017cemy stosowa\u0107 r\u00f3\u017cne rodzaje nadmiarowo\u015bci, aby naprawia\u0107 b\u0142\u0119dy.<br \/>\nKody Hamming'a mog\u0105 naprawia\u0107 pojedyncze b\u0142\u0119dy bitowe, kody Reed-Solomon'a s\u0105 symboliczne, a kilka kopii danych w po\u0142\u0105czeniu z sumami kontrolnymi czy kodowaniem typu RAID-6 mog\u0105 pom\u00f3c w odzyskaniu danych nawet w przypadku masowych uszkodze\u0144.<br \/>\nPocz\u0105tkowo by\u0142em nastawiony na szerokie wykorzystanie kodowania odpornych na zak\u0142\u00f3cenia, ale potem zrozumia\u0142em, \u017ce najpierw nale\u017cy mie\u0107 wyobra\u017cenie o tym, przed jakimi b\u0142\u0119dami chcemy si\u0119 chroni\u0107, a p\u00f3\u017aniej wybiera\u0107 kodowanie.<\/p>\n<p><\/p>\n<p>M\u00f3wili\u015bmy wcze\u015bniej, \u017ce b\u0142\u0119dy nale\u017cy identyfikowa\u0107 jak najszybciej. W jakich momentach mo\u017cemy si\u0119 spotka\u0107 z b\u0142\u0119dami?<\/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 \/>\nNiestety, w przypadku takiego b\u0142\u0119du pozostaje tylko zignorowa\u0107 nieprawid\u0142owe zapisy i uzna\u0107 dane za utracone;<\/li>\n<li>B\u0142\u0119dy zapisu (z jakiego\u015b powodu do pami\u0119ci flash zapisano co\u015b innego ni\u017c to, co zapisano)<br \/>\nTakie b\u0142\u0119dy mo\u017cemy od razu wykry\u0107, je\u015bli bezpo\u015brednio po zapisie dokonamy kontroli odczytu;<\/li>\n<li>Zniekszta\u0142cenie danych w pami\u0119ci w trakcie przechowywania;<\/li>\n<li>B\u0142\u0119dy odczytu<br \/>\nAby naprawi\u0107 wystarczaj\u0105co, w przypadku niezgodno\u015bci sumy kontrolnej, wystarczy kilkakrotnie powt\u00f3rzy\u0107 odczyt.<\/li>\n<\/ol>\n<p><\/p>\n<p>Oznacza to, \u017ce tylko b\u0142\u0119dy trzeciego typu (samoistne uszkodzenie danych podczas przechowywania) nie mog\u0105 by\u0107 naprawione bez kodowania odpornych na zak\u0142\u00f3cenia. Uwa\u017cam, \u017ce takie b\u0142\u0119dy s\u0105 jednak niezwykle ma\u0142o prawdopodobne.<\/p>\n<p><\/p>\n<p><strong>Podsumowanie:<\/strong> postanowiono zrezygnowa\u0107 z nadmiarowego kodowania, ale je\u015bli eksploatacja wyka\u017ce b\u0142\u0119dno\u015b\u0107 tej decyzji, to wr\u00f3cimy do rozwa\u017cenia tej kwestii (z ju\u017c zgromadzon\u0105 statystyk\u0105 o awariach, kt\u00f3ra pozwoli wybra\u0107 optymalny typ kodowania).<\/p>\n<p><\/p>\n<h3 id=\"prochee\">Inne<\/h3>\n<p><\/p>\n<p>Oczywi\u015bcie format artyku\u0142u nie pozwala uzasadni\u0107 ka\u017cdego bitu w formacie <em>(a ja ju\u017c r\u00f3wnie\u017c straci\u0142em si\u0142y)<\/em>, dlatego kr\u00f3tko przelotnie przejd\u0119 przez niekt\u00f3re kwestie, kt\u00f3re nie zosta\u0142y wcze\u015bniej poruszone.<\/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 \/>\nNie b\u0119dzie specjalnych stron z metadanymi, oddzielnymi strumieniami itd., zamiast tego pojawi si\u0119 jeden strumie\u0144, kt\u00f3ry przepisuje wszystkie strony kolejno.<br \/>\nZapewnia to r\u00f3wnomierne zu\u017cycie stron, brak jednego punktu awarii, a po prostu si\u0119 podoba;<\/li>\n<li>Nale\u017cy wprowadzi\u0107 wersjonowanie formatu.<br \/>\nFormat bez numeru wersji w nag\u0142\u00f3wku to z\u0142o!<br \/>\nWystarczy doda\u0107 do nag\u0142\u00f3wka strony pole z jakim\u015b Magic Number (sygnatur\u0105), kt\u00f3re b\u0119dzie wskazywa\u0107 na u\u017cywan\u0105 wersj\u0119 formatu. <em>(nie s\u0105dz\u0119, aby w praktyce by\u0142o ich nawet dziesi\u0119\u0107)<\/em>;<\/li>\n<li>U\u017cywa\u0107 zmiennej d\u0142ugo\u015bci nag\u0142\u00f3wk\u00f3w dla rekord\u00f3w (kt\u00f3rych jest bardzo du\u017co), staraj\u0105c si\u0119 w wi\u0119kszo\u015bci przypadk\u00f3w uczyni\u0107 je o d\u0142ugo\u015bci 1 bajta;<\/li>\n<li>Do kodowania d\u0142ugo\u015bci nag\u0142\u00f3wka i d\u0142ugo\u015bci obcinanej cz\u0119\u015bci skompresowanego rekordu u\u017cywa\u0107 binarnych kod\u00f3w zmiennej d\u0142ugo\u015bci.<\/li>\n<\/ul>\n<p><\/p>\n<p>Bardzo pom\u00f3g\u0142 <noindex><a rel=\"nofollow\" href=\"https:\/\/planetcalc.com\/2481\/\">generator online<\/a><\/noindex> kod\u00f3w Huffmana. Dos\u0142ownie w kilka minut uda\u0142o si\u0119 dopasowa\u0107 potrzebne kody zmiennej d\u0142ugo\u015bci.<\/p>\n<p><\/p>\n<h1 id=\"anchorformatanchoropisanie-formata-hraneniya-dannyh\"><noindex><a rel=\"nofollow\" name=\"format\"><\/a><\/noindex>Opis formatu przechowywania danych<\/h1>\n<p><\/p>\n<h2 id=\"byte-order\">Kolejno\u015b\u0107 bajt\u00f3w<\/h2>\n<p><\/p>\n<p>Pola wi\u0119ksze ni\u017c jeden bajt s\u0105 przechowywane w formacie big-endian (network byte order), co oznacza, \u017ce 0x1234 jest zapisywane jako 0x12, 0x34.<\/p>\n<p><\/p>\n<h2 id=\"delenie-na-stranicy\">Podzia\u0142 na strony<\/h2>\n<p><\/p>\n<p>Ca\u0142a pami\u0119\u0107 flash jest podzielona na strony o r\u00f3wnej wielko\u015bci.<\/p>\n<p><\/p>\n<p>Domy\u015blny rozmiar strony to 32 KB, ale nie wi\u0119cej ni\u017c 1\/4 ca\u0142kowitego rozmiaru uk\u0142adu pami\u0119ci (dla uk\u0142adu 4 MB wypada 128 stron).<\/p>\n<p><\/p>\n<p>Ka\u017cda strona przechowuje dane niezale\u017cnie od innych (to znaczy dane jednej strony nie odnosz\u0105 si\u0119 do danych innej strony).<\/p>\n<p><\/p>\n<p>Wszystkie strony s\u0105 ponumerowane w naturalnej kolejno\u015bci (w porz\u0105dku rosn\u0105cym adres\u00f3w), zaczynaj\u0105c od numeru 0 (strona zerowa zaczyna si\u0119 od adresu 0, pierwsza \u2014 od 32 KB, druga \u2014 od 64 KB itd.)<\/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\">Wewn\u0105trz strony<\/h2>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci 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>Nag\u0142\u00f3wek strony (na schemacie w kolorze brudnozielonym) sk\u0142ada si\u0119 z:<\/p>\n<p><\/p>\n<ul>\n<li>dwubajtowego pola Magic Number (to tak\u017ce \u2014 oznaczenie wersji formatu)<br \/>\ndla bie\u017c\u0105cej wersji formatu jest uwa\u017cany za <code>0xed00 \u2295 numer strony<\/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>Wpisy na stronie przechowywane s\u0105 w skompresowanej postaci (u\u017cywany jest algorytm deflate). Wszystkie wpisy na jednej stronie kompresowane s\u0105 w jednym strumieniu (stosuje si\u0119 wsp\u00f3lny s\u0142ownik), na ka\u017cdej nowej stronie kompresja zaczyna si\u0119 od nowa. Oznacza to, \u017ce do dekompresji dowolnego wpisu wymagane s\u0105 wszystkie wcze\u015bniejsze wpisy z tej strony (i tylko z tej).<\/p>\n<p><\/p>\n<p>Ka\u017cdy wpis zostanie skompresowany z flag\u0105 Z_SYNC_FLUSH, przy czym na ko\u0144cu skompresowanego strumienia znajduj\u0105 si\u0119 4 bajty 0x00, 0x00, 0xff, 0xff, poprzedzone, mo\u017cliwie, jeszcze jednym lub dwoma zerowymi bajtami.<br \/>\nT\u0119 sekwencj\u0119 (o d\u0142ugo\u015bci 4, 5 lub 6 bajt\u00f3w) odrzucamy podczas zapisu do pami\u0119ci flash.<\/p>\n<p><\/p>\n<p>Nag\u0142\u00f3wek wpisu sk\u0142ada si\u0119 z 1, 2 lub 3 bajt\u00f3w, kt\u00f3re przechowuj\u0105:<\/p>\n<p><\/p>\n<ul>\n<li>jeden bit (T), oznaczaj\u0105cy typ wpisu: 0 \u2014 kontekst, 1 \u2014 dziennik;<\/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>d\u0142ugo\u015b\u0107 wpisu (L).<\/li>\n<\/ul>\n<p><\/p>\n<p>Tabela warto\u015bci S:<\/p>\n<p><\/p>\n<p>S<br \/>\nD\u0142ugo\u015b\u0107 nag\u0142\u00f3wka, bajty<br \/>\nOdrzuca si\u0119 przy zapisie, bajty<\/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>Spr\u00f3bowa\u0142em to zilustrowa\u0107, nie wiem, jak dobrze to wysz\u0142o:<br \/>\n<img decoding=\"async\" alt=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/8c9b739eec5af4429395c32b4ba4044a.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n\u017b\u00f3\u0142tym tutaj oznaczone pole T, bia\u0142ym \u2014 pole S, zielonym L (d\u0142ugo\u015b\u0107 skompresowanych danych w bajtach), niebieskim \u2014 skompresowane dane, czerwonym \u2014 ko\u0144cowe bajty skompresowanych danych, kt\u00f3re nie s\u0105 zapisywane do pami\u0119ci flash.<\/p>\n<p><\/p>\n<p>W ten spos\u00f3b nag\u0142\u00f3wki wpis\u00f3w o najcz\u0119stszej d\u0142ugo\u015bci (do 63+5 bajt\u00f3w w skompresowanej postaci) b\u0119dziemy mogli zapisa\u0107 jednym bajtem.<\/p>\n<p><\/p>\n<p>Po ka\u017cdym wpisie przechowywana jest suma kontrolna CRC-32C, kt\u00f3rej jako warto\u015b\u0107 pocz\u0105tkowa (init) u\u017cywane jest odwr\u00f3cone warto\u015b\u0107 poprzedniej sumy kontrolnej.<\/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=\"Moja implementacja bufora ringowego w pami\u0119ci NOR flash\" src=\"\/wp-content\/uploads\/2019\/12\/b194d6a3d18ae2f4ae5d4054a93c0ea3.jpg\" style=\"display:block;margin: 0 auto;\" \/>.<br \/>\nOznacza to, \u017ce faktycznie obliczamy CRC wszystkich poprzednich bajt\u00f3w nag\u0142\u00f3wk\u00f3w i danych na tej stronie.<\/em><\/p>\n<p><\/p>\n<p>Bezpo\u015brednio za sum\u0105 kontroln\u0105 znajduje si\u0119 nag\u0142\u00f3wek nast\u0119pnego wpisu.<\/p>\n<p><\/p>\n<p>Nag\u0142\u00f3wek skonstruowany jest w taki spos\u00f3b, \u017ce jego pierwszy bajt zawsze r\u00f3\u017cni si\u0119 od 0x00 i 0xff (je\u015bli zamiast pierwszego bajtu nag\u0142\u00f3wka spotykamy 0xff, to oznacza to nieu\u017cywan\u0105 przestrze\u0144; 0x00 sygnalizuje b\u0142\u0105d).<\/p>\n<p><\/p>\n<h2 id=\"primernye-algoritmy\">Przyk\u0142adowe algorytmy<\/h2>\n<p><\/p>\n<h3 id=\"chtenie-iz-flesh-pamyati\">Odwo\u0142anie do pami\u0119ci flash<\/h3>\n<p><\/p>\n<p>Ka\u017cde odczytanie odbywa si\u0119 z weryfikacj\u0105 sumy kontrolnej.<br \/>\nJe\u015bli suma kontrolna si\u0119 nie zgadza, odczyt jest powtarzany kilka razy w nadziei na odczytanie poprawnych danych.<\/p>\n<p><\/p>\n<p><em>(to ma sens, Linux nie buforuje odczytu z NOR Flash, sprawdzone)<\/em><\/p>\n<p><\/p>\n<h3 id=\"zapis-v-flesh-pamyat\">Zapisywanie do pami\u0119ci flash<\/h3>\n<p><\/p>\n<p>Zapisujemy dane.<br \/>\nOdczytujemy je.<\/p>\n<p><\/p>\n<p>Je\u015bli odczytane dane nie zgadzaj\u0105 si\u0119 z zapisanymi, wype\u0142niamy obszar zerami i sygnalizujemy b\u0142\u0105d.<\/p>\n<p><\/p>\n<h3 id=\"podgotovka-novoy-mikroshemy-k-rabote\">Przygotowanie nowego uk\u0142adu do pracy<\/h3>\n<p><\/p>\n<p>W celu inicjalizacji w pierwsz\u0105 (a w\u0142a\u015bciwie zerow\u0105) stron\u0119 zapisywany jest nag\u0142\u00f3wek z wersj\u0105 1.<br \/>\nPo tym do tej strony zapisywany jest pocz\u0105tkowy kontekst (zawiera UUID automatu i domy\u015blne ustawienia). <\/p>\n<p><\/p>\n<p>I wszystko, pami\u0119\u0107 flash gotowa do pracy.<\/p>\n<p><\/p>\n<h3 id=\"zagruzka-avtomata\">\u0141adowanie automatu<\/h3>\n<p><\/p>\n<p>Podczas \u0142adowania odczytywane s\u0105 pierwsze 8 bajt\u00f3w ka\u017cdej strony (nag\u0142\u00f3wek + CRC), strony z nieznanym Magic Number lub b\u0142\u0119dnym CRC s\u0105 ignorowane.<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 \/>\nZapisujemy s\u0142ownik zlib (b\u0119dzie potrzebny do dopisywania do tej strony).<\/p>\n<p><\/p>\n<p>I wszystko, \u0142adowanie zako\u0144czone, kontekst odzyskany, mo\u017cna pracowa\u0107.<\/p>\n<p><\/p>\n<h3 id=\"dobavlenie-zapisi-v-zhurnal\">Dodawanie wpisu do dziennika<\/h3>\n<p><\/p>\n<p>Kompresujemy wpis z poprawnym s\u0142ownikiem, wskazuj\u0105c Z_SYNC_FLUSH. Sprawdzamy, czy skompresowany wpis zmie\u015bci si\u0119 na bie\u017c\u0105cej stronie.<br \/>\nJe\u015bli si\u0119 nie mie\u015bci (lub na stronie by\u0142y b\u0142\u0119dy CRC) \u2014 zaczynamy now\u0105 stron\u0119 (patrz poni\u017cej).<br \/>\nZapisujemy wpis i CRC. Je\u015bli wyst\u0105pi\u0142 b\u0142\u0105d \u2014 zaczynamy now\u0105 stron\u0119.<\/p>\n<p><\/p>\n<h3 id=\"novaya-stranica\">Nowa strona<\/h3>\n<p><\/p>\n<p>Wybieramy woln\u0105 stron\u0119 z minimalnym numerem (woln\u0105 uznajemy stron\u0119 z b\u0142\u0119dn\u0105 sum\u0105 kontroln\u0105 w nag\u0142\u00f3wku lub z wersj\u0105 mniejsz\u0105 ni\u017c bie\u017c\u0105ca). Je\u015bli takich stron nie ma \u2014 wybieramy stron\u0119 z minimalnym numerem z tych, kt\u00f3re maj\u0105 wersj\u0119 r\u00f3wn\u0105 bie\u017c\u0105cej.<br \/>\nWybieramy stron\u0119 do wymazania. Por\u00f3wnujemy zawarto\u015b\u0107 z 0xff. Je\u015bli co\u015b jest nie tak, bierzemy nast\u0119pn\u0105 woln\u0105 stron\u0119 itd.<br \/>\nNa wymazanej stronie zapisujemy nag\u0142\u00f3wek, pierwszym wpisem aktualny stan kontekstu, kolejnym \u2014 niezapisany wpis dziennika (je\u015bli taki istnieje).<\/p>\n<p><\/p>\n<h1 id=\"primenimost-formata\">Zastosowanie formatu<\/h1>\n<p><\/p>\n<p>Moim zdaniem, to niez\u0142y format do przechowywania wszelkich bardziej lub mniej kompresowalnych strumieni informacji (zwyk\u0142y tekst, JSON, MessagePack, CBOR, a mo\u017ce protobuf) w pami\u0119ci 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>Nie nale\u017cy go u\u017cywa\u0107 z no\u015bnikami o wysokim BER, np. NAND lub MLC NOR. <em>(czy taka pami\u0119\u0107 w og\u00f3le jest dost\u0119pna w sprzeda\u017cy? Spotyka\u0142em si\u0119 jedynie z odniesieniami w pracach na temat kod\u00f3w korekcji)<\/em>.<\/p>\n<p><\/p>\n<p>Tym bardziej nie wolno go u\u017cywa\u0107 z urz\u0105dzeniami, kt\u00f3re maj\u0105 w\u0142asne FTL: USB flash, SD, MicroSD itd. <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>W zale\u017cno\u015bci od zada\u0144, format mo\u017cna bez zmian u\u017cywa\u0107 na pendrive'ach od 128 Kbit (16 KB) do 1 Gbit (128 MB). W razie potrzeby mo\u017cna go te\u017c wykorzysta\u0107 na wi\u0119kszych chipach, tylko prawdopodobnie trzeba dostosowa\u0107 rozmiar strony. <em>(Ale tutaj pojawia si\u0119 ju\u017c pytanie o rentowno\u015b\u0107, cena du\u017cych pami\u0119ci NOR Flash nie zachwyca)<\/em>.<\/p>\n<p><\/p>\n<p>Je\u015bli kto\u015b uzna\u0142 format za interesuj\u0105cy i chcia\u0142by go wykorzysta\u0107 w otwartym projekcie \u2014 piszcie, postaram si\u0119 znale\u017a\u0107 czas, uporz\u0105dkowa\u0107 kod i udost\u0119pni\u0107 na githubie.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Podsumowanie<\/h1>\n<p><\/p>\n<p>Jak wida\u0107, ostatecznie format okaza\u0142 si\u0119 prosty. <em>I nawet nudny.<\/em>.<\/p>\n<p><\/p>\n<p>W artykule trudno odda\u0107 ewolucj\u0119 swojego punktu widzenia, ale uwierzcie: na pocz\u0105tku chcia\u0142em stworzy\u0107 co\u015b skomplikowanego, niezniszczalnego, zdolnego przetrwa\u0107 nawet po wybuchu j\u0105drowym w bezpo\u015bredniej blisko\u015bci. Jednak rozum (mam nadziej\u0119) w ko\u0144cu zwyci\u0119\u017cy\u0142 i stopniowo priorytety przesun\u0119\u0142y si\u0119 w stron\u0119 prostoty i kompaktowo\u015bci.<\/p>\n<p><\/p>\n<p>Czy mo\u017ce si\u0119 zdarzy\u0107, \u017ce si\u0119 myli\u0142em? Tak, oczywi\u015bcie. Mo\u017ce si\u0119 okaza\u0107, na przyk\u0142ad, \u017ce zakupili\u015bmy parti\u0119 wadliwych chip\u00f3w. Lub z jakiego\u015b innego powodu sprz\u0119t nie spe\u0142ni oczekiwa\u0144 dotycz\u0105cych niezawodno\u015bci.<\/p>\n<p><\/p>\n<p>Czy mam plan na t\u0119 ewentualno\u015b\u0107? My\u015bl\u0119, \u017ce po przeczytaniu artyku\u0142u nie Macie w\u0105tpliwo\u015bci, \u017ce plan istnieje. I to nie jeden.<\/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>Na dzie\u0144 dzisiejszy wszystko dzia\u0142a dobrze, dos\u0142ownie w ci\u0105gu kilku dni rozwi\u0105zanie b\u0119dzie wdro\u017cone. <em>(oko\u0142o)<\/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>(a je\u015bli nie b\u0119dzie szcz\u0119\u015bcia \u2014 to i wcze\u015bniej)<\/em>.<\/p>\n<p><\/p>\n<p>Je\u015bli po zako\u0144czeniu u\u017cytkowania pojawi\u0105 si\u0119 powa\u017cne problemy i b\u0119d\u0105 potrzebne poprawki, na pewno o tym napisz\u0119.<\/p>\n<p><\/p>\n<h1 id=\"literatura\">Literatura<\/h1>\n<p><\/p>\n<p>Nie chcia\u0142em tworzy\u0107 d\u0142ugiej, nudnej listy wykorzystanych prac, w ko\u0144cu Google jest dost\u0119pny dla wszystkich.<\/p>\n<p><\/p>\n<p>Postanowi\u0142em zostawi\u0107 list\u0119 odkry\u0107, kt\u00f3re wyda\u0142y mi si\u0119 szczeg\u00f3lnie interesuj\u0105ce, jednak stopniowo przenios\u0142y si\u0119 one bezpo\u015brednio do tekstu artyku\u0142u, a w li\u015bcie pozosta\u0142 tylko jeden punkt:<\/p>\n<p><\/p>\n<ol>\n<li>Narz\u0119dzie <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/madler\/infgen\/\">infgen<\/a><\/noindex> od autora zlib. Umie w zrozumia\u0142y spos\u00f3b wy\u015bwietla\u0107 zawarto\u015b\u0107 archiw\u00f3w deflate\/zlib\/gzip. Je\u015bli musisz zg\u0142\u0119bia\u0107 wewn\u0119trzn\u0105 budow\u0119 formatu deflate (lub gzip) \u2014 gor\u0105co polecam.<\/li>\n<\/ol>\n<p>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/479044\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435. \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u043c\u043e\u043d\u0435\u0442\u043e\u043f\u0440\u0438\u0451\u043c\u043d\u0438\u043a, \u043a\u0443\u043f\u044e\u0440\u043e\u043f\u0440\u0438\u0451\u043c\u043d\u0438\u043a, \u0431\u0430\u043d\u043a\u043e\u0432\u0441\u043a\u0438\u0439 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u2026 \u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u0441\u0435\u043c \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430. \u0412\u0441\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u043f\u0438\u0448\u0435\u0442\u0441\u044f \u0432 \u0436\u0443\u0440\u043d\u0430\u043b \u043d\u0430 \u0444\u043b\u0435\u0448\u043a\u0435 (MicroSD), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 (\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e USB-\u043c\u043e\u0434\u0435\u043c\u0430) \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0442\u0430\u043c \u0441\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0411\u0414. \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u0440\u043e\u0434\u0430\u0436\u0430\u0445 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u0432 1\u0441, \u0442\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-53751","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u041c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u043b\u044c\u0446\u0435\u0432\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430 \u0432 NOR flash | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/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\udd47Moja implementacja bufora pier\u015bcieniowego w pami\u0119ci NOR flash | ProHoster","description":"T\u0142o S\u0105 to automaty sprzedaj\u0105ce opracowane przez nas. W \u015brodku Raspberry Pi i troch\u0119 okablowania na oddzielnej p\u0142ycie.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u041c\u043e\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043a\u043e\u043b\u044c\u0446\u0435\u0432\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430 \u0432 NOR flash | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u044b\u0441\u0442\u043e\u0440\u0438\u044f \u0415\u0441\u0442\u044c \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0435 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u044b \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u0412\u043d\u0443\u0442\u0440\u0438 Raspberry Pi \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0431\u0432\u044f\u0437\u043a\u0438 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u043b\u0430\u0442\u0435.","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/moya-realizatsiya-koltsevogo-bufera-v-nor-flash","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-12-08T21:00:00+00:00","article:modified_time":"2020-02-18T11:01:41+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"53751","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-24 08:36:23","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:18:30","updated":"2026-01-24 08:36:23","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/53751","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=53751"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/53751\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=53751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=53751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=53751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}