Как стать автором
Обновить

Комментарии 50

Ни с одной ищ вышеперечисленных "проблем" ниразу не сталкивался. Мне кажется вы на ровном месте выдумали проблему и сами же её решили

Аналогичные мысли. Нам нужно записать переменную в энергонезависимую память? А давайте сделаем это наиболее извращенным и непрактичным способом!

Проблемы возникают, когда вы делаете что-то то не для себя. Это и называется промышленным качеством.

Прерывание по питанию + резервный источник питания (конденсатор) + запись с сохранением timestamp и CRC поблочно (раз хочется транзакций) и каждый раз в другую область памяти. При восстановлении ищется самый новый валидный блок данных.
Я бы так делал.

Да, можно и так. Хочется чего то простого и удобного в использовании.

просто, удобно, надёжно — выберите любые 2

Промышленное качество на ардуино? Серьезно?
А почему нет? Железо не хуже любого другого, а надежность софта в руках писателя.
Железо нормальное но не для промышленного качества
Дальше неплохо было бы определить, что же такое промышленное качества и почему его не хватает Адурине.

Зачем попытки объяснить ACID-принцип языком домохозяек? Имхо, все эти примеры с посмертными завещаниями и нотариусами только снижают качество материала — вместо них можно было бы огласить условия, при которых целостность данных могла быть нарушена. Технически же решение корректное, и с однобитной эпохой — красивое. Нужно только быть уверенным, что принцип устойчив к частичной записи байта (питание или ресет может прилететь в совершенно произвольный момент)

Это была попытка объяснить тем, кто не знает про ACID. Тем, кто знает, ничего объяснять не нужно. От частичной записи защищает контрольная сумма. Можно конечно бит корректности стирать и записывать последним, но EEPROM жалко.
Идея правильная, но с имеющимся API будет непросто раскидать переменные по разным, в том числе и сторонним модулям. Кроме того, при достаточном количестве переменных очень легко разрушить всю структуру изменив размер одной переменной. Из последнего вытекают и сложности с условной компиляцией.
Да, выбрасывание переменной или изменение ее размера автоматически сдвинет все последующие, и они перестанут читаться. Но код все равно должен быть готов к тому, что они не читаются, так что тут особой проблемы нет. Главное, что ничего не сломается и не прочитается неверно.
Если после такой дефектной прошивки слетят настройки, то в этом нет ничего хорошего, т.к. даже исправленная программа уже их не вернет, а устройство будет простаивать.
Хотите сделать совместимый софт — просто не выбрасывайте из него то, что уже зарелизили. Это вообще универсальный принцип.
Тем не менее, если реализовать эмуляцию каталога и в нем хранить ссылки на данные, то такой проблемы можно избежать.
Решение слишком не универсальное. Переменная которая будет писаться в одно и то же место быстро исчерпает ресурс eeprom — поэтому это подходит только для достаточно редкой записи или небольшого lifetime устройства. Далее если у нас какой то условный конфиг в еепром который мы условно 1-2-3 раза записываем и 100500 раз читаем — то 2 не совпадающие копии никуда не годятся, нужно нечетное число с голосованием и восстановлением битых копий… Далее у вас информация о записи содержится как в instance id так и в адресе блока — не понятен смысл такого дублирования если только у вас eeprom не висит на параллельной адресной шине с вероятностью отказа одной из линий… копии лежат друг за другом — это не очень хорошо, их надо держать как минимум в разных секторах… Опять же надежная работа с eeprom начинается с контроля питания и обеспечения завершения всех операций при аварии питания, и запрета начала новых которые могут быть не завершены — т.е. начинать надо не с софта а с схемы формирования питания и определения времени гарантированной работы системы при этой аварии.
У Таненбаума в «Архитектуре компьютера» есть целая глава про организацию отказоустойчивых и транзакционных хранилищ. Очень советую.

Если хотите атомарности записи и иметь записи в виде однородного массива, можете реализовать следующую схему (одна из простых):
1. Структура имеет два байтовых счётчика в начале и конце и payload посередине.
2. Для перезаписываемой ячейки считываете текущие значения счётчиков (они могут быть либо одинаковыми, либо отличаться)
3. Если они одинаковые, запись достоверная, читаем payload. Можно сделать 0 спецзначением «нет данных после стирания хранилища», но это не принципиально.
4. Увеличиваете первый счётчик на единицу или на 2 так, чтобы он гарантированно не совпал со вторым. Записываете полученное новое значение в первый счётчик. Если 0 — спецзначение, то пропускаете его в случае переполнения.
5. Записываете payload.
6. Закрываете транзакцию записью второго счётчика.
Счётчики, строго говоря, не обязательно должны находиться в разных концах записи — важна лишь последовательность их записи.

Прекрасный алгоритм, без ненужных излишеств. Единственное но — лучше 0xff тоже зарезервировать; в Atmega eeprom после стирания возвращает 1.

два счётчика по сути защищают лишь от "запись была прервана" (питание пропало, etc).


crc мне нравится куда больше, защищает от практически любых повреждений.

Тут нужно ответить на дополнительные вопросы
1. Откуда возьмутся любые повреждения? Память глючная? Радиация? Тогда до расчета CRC ваша программа может не дожить.
2. Допустим, CRC не совпадает. Что делать дальше, если при неудачном стечении обстоятельств данные внутри устройства теряются?
Главное, что CRC делает лучше, чем пара счетчиков, — помогает отличить сохраненные данные от неинициализированных. Вероятность случайного совпадения 2-х однобайтных счетчиков 1/256, а вероятность случайного совпадения 2-х байтной контрольной суммы — 1/65536. Что делать если не сошлась? Инициализировать значениями по умолчанию.

Не нужно путать контроль целостности (для этого годится CRC) и атомарность записи (для этого флажки-счетчики размером в 1 байт — eeprom в avr работает с байтами — отлично подходят).

и чем (кроме размера) плохи многобитные crc для контроля атомарности записи?

Ну, например тем, что их запись неатомарна?
Ну и чем плоха неатомарность записи CRC? Если запись завершилась, мы получим корректно сохраненные данные, если нет — мы это обнаружим, CRC то не сойдется в итоге.

Может и сойтись. Откуда вы знаете, что находится в той области, куда CRC не дописалось? Без понимания, что именно и почему может испортиться, контрольная сумма может и не помочь. А то и навредить; при аварийном сохранении данных на расчеты может не хватить времени.

Не нужно путать контроль целостности (для этого годится CRC) и атомарность записи (для этого флажки-счетчики размером в 1 байт — eeprom в avr работает с байтами — отлично подходят).
1. Не нужно путать, нужно знать. Что CRC8 имеет размер ровно в один байт и гораздо информативнее 'флажка-счетчика'.
2. Атомарность? Сколько команд 'avr'(?) требуется для записи одного байта в EEPROM? Сколько времени занимает запись одного байта?
Может и сойтись. Откуда вы знаете, что находится в той области, куда CRC не дописалось?
1. Может. Какова вероятность?
2. Ничего не находится, пусто там. Предварительная запись 0xFF ресурс не расходует. Но это не важно при правильной финализации. Не?
А то и навредить; при аварийном сохранении данных на расчеты может не хватить времени.
1. Не покажите, где автор говорил про аварийное сохранение?
2. Спрошу повторно, сколько времени 'avr'(?) записывает один байт в EEPROM? Сколько за это время можно сделать 'расчетов'?

Извините, не удержался, заглянул в ваш профиль.
CRC8 имеет размер ровно в один байт и гораздо информативнее 'флажка-счетчика'


2 однобайтовых счетчика совершенно однозначно (с вероятностью 1/254/254 где-то; значения 0х00 и 0xff можно исключить) укажут, что блок данных дописан до конца. CRC8 совпадет с вероятностью 1/256 на любых данных. CRC16 совпадет с вероятностью 1/65536, то есть для поддержки транзакционной записи особенной разницы нет — CRC16 или 2 счетчика.Но счетчики в 8-битном контроллере немного быстрее ;)

Сколько команд 'avr'(?) требуется для записи одного байта в EEPROM? Сколько времени занимает запись одного байта?


Много и долго

Но один байт либо запишется весь, либо не запишется тоже весь. Более атомарной записи на avr не выйдет.

где автор говорил про аварийное сохранение?

Ну ок. Зачем еще может понадобиться в однозадачной среде транзакционная запись?
то есть для поддержки транзакционной записи особенной разницы нет — CRC16 или 2 счетчика.Но счетчики в 8-битном контроллере немного быстрее ;)
Не смешно, по причинам:
1. Главное галочку поставить, а целостность содержимого пакета вас не интересует? Способы испортить пакет после его записи в еепром перечислить?
2. Ещё раз спрошу, за время записи одного байта в еепром, сколько раз можно CRC пакета табличным способом подсчитать?
3. Андуина — это только ATMega8? Про ардуины на STM32 не слышали, не? И про их аппаратный модуль расчёта CRC тоже не в курсе?
4. Сохранение данных в еепром эксклюзивно в ардуине используется, другие МК их на бумажку записывают?
5. Просто любопытно, а где в МК счётчики, которые 'немного быстрее'?
Ну ок. Зачем еще может понадобиться в однозадачной среде транзакционная запись?
Это фейспалм! Извините великодушно, не удержался. И действительно, зачем?

К тому же с чего вы решили что она однозадачная? Это где-то оговаривалось? RTOS на ATMega8 никто не запрещал.

Но один байт либо запишется весь, либо не запишется тоже весь.
Мне этот аргумент кажется очень притянутым. При таком времени записи пропадание/просадка питания имеет далеко ненулевую вероятность. Но к теме это отношения не имеет.

"Способы испортить пакет после его записи в еепром перечислить?"


Конечно. С этого нужно начинать ;)


"где в МК счётчики, которые 'немного быстрее'?"


Я программист ненастоящий, ардуину на помойке нашел, но мне кажется, что i++ && eeprom_write_byte никак не медленнее чем calc_crc16 && eeprom_write_2_byte.

А вот если crc16 с помощью паддинга подгонять к значению, когда оба байта отличаются от предыдущих, в начале блока данных писать 1 байт а в конце 2-й — будет и транзакционность, и контроль целостности и в гамаке и стоя :)
  1. Да, вы назвали некоторые причины. Если вкратце, то не бывает BER=0, если при эксплуатации вы не встречали ошибок — значит просто выборка была недостаточно велика.
    Не зря же в серверах используется память с ECC, в ZFS ещё много лет назад Sun добавило контрольные суммы для всего, и т.д., и т.п.
  2. Правильный вопрос. Одно из решений: CoW, изменение данных не затирает предыдущие, а создаёт новую версию. При чтении "битые" версии просто отбрасываются, то есть мы откатываемся на последнюю неповреждённую версию. Да, не очень приятно, но лучше, чем ничего.
    Другое решение — добавление избыточности в сами записываемые данные (в простейшем случае — хранение нескольких копий).
Кстати, смежный вопрос: а как гарантировать корректность реализации любого из выбранных алгоритмов? Не тестированием же, в конце концов, не?
CoW не слишком хорошо работает на ограниченных ресурсах. Та же EEPROM может вместить ну пускай несколько десятков или сотен записей. Дальше надо как-то старые затирать, организовывать кольцевой буфер и делать прочие жуткие вещи.

Ну да, нужно организовывать, что тут жуткого-то? Кольцевой буфер с фиксированным размером записей реализуется буквально в десяток-другой строк на C.


BTW, как раз недавно писал статью о похожем случае. Конечно, другие размеры — другая реализация, но проблемы и подходы к их решению вполне актуальны и для этой задачи.

В серверной памяти (большой объем, огромный uptime) ECC нужен по одной причине. В файловой системе (где в любом случае есть битые сектора) причины другие. В микроконтроллере проблемы с памятью тоже могут иметь свои причины (портится 1 ячейка? Область ячеек? Ряд ячеек? Постоянный дефект или плавающий?). Без учёта этих особенностей любое усложнение — карго культ, который может сработать, может не сработать, и эффективность его очень сложно оценить.

В серверной памяти (большой объем, огромный uptime) ECC нужен по одной причине. В файловой системе (где в любом случае есть битые сектора) причины другие

Да нет, причина по сути одна и та же — иногда мы можем прочитать не те данные, что должны быть записаны. Эти случаи нам нужно детектировать и как-то обрабатывать.
И с хранением данных в EEPROM то же самое.

иногда мы можем прочитать не те данные, что должны быть записаны. Эти случаи нам нужно детектировать и как-то обрабатывать.


Для регистров процессора и ОЗУ (с помощью которых считают CRC) это утверждение тоже справедливо? Если да, то надеяться на адекватность работы CRC как-то самонадеянно. Если же это относится только к EEPROM — надо начинать с datasheet'ов. Там должно быть про это написано. Или не написано ;) — тогда вы начинаете решать придуманную проблемму.
Если же это относится только к EEPROM — надо начинать с datasheet'ов. Там должно быть про это написано. Или не написано ;)
Действительно, может перед тем как разводить флейм, стоит почитать даташиты, начать с AVR104 например? Или спросить у гугла 'atmega eeprom проблемы'?

Или посмотреть ваш профиль.
тогда вы начинаете решать придуманную проблемму.
Автор затронул реальную проблему, на которую новички не обращают внимания. Привязывать её к Ардуине необязательно, еепром может быть внешней, или её может не быть совсем и использоваться часть флэша для сохранения данных. Проблем с ними ещё больше.
Для регистров процессора и ОЗУ (с помощью которых считают CRC) это утверждение тоже справедливо?

Отчасти, да. Для 8-битников это не столь актуально, а для более сложных кристаллов, да. Например, для кортексов m3 есть аппаратный обработчик «Memory management fault», не говоря уже о прочих аппаратных обработчиках исключений.

Проблема не надумана. Начнем с того, что данные в EEPROM сохраняются не вечно. Производители не нормируют длительность времени сохранения данных.
Так же реально может произойти недостоверность записи данных, например, при пониженном питании. Влияет и количество записей/стираний, и условия эксплуатации.

Да же если данные с crc или счетчиками были успешно записаны и тут же проверены, со временем они могут пропасть/исказиться в силу физики EEPROM. Поэтому счетчики тут не помогут, например, записали — прочитали — все в норме, а со временем информация исказиться. А вот crc или хэш с очень высокой долей вероятности выявит искажения в данных. CRC защищает именно данные, а счетчики позволяют сказать только что данные сохранились.
Начнем с того, что данные в EEPROM сохраняются не вечно. Производители не нормируют длительность времени сохранения данных.
Так же реально может произойти недостоверность записи данных, например, при пониженном питании. Влияет и количество записей/стираний, и условия эксплуатации.


Возражений по существу нет ;) Все эти факторы могут как-то влиять на что-то. Но все это общие слова. Никаких цифр у нас нет. Оценить существенна ли проблема, или нет, мы не можем, но пару способов решения уже придумали ;)
Но все это общие слова. Никаких цифр у нас нет.

Количество рекламаций от конечных пользователей 'глючных' девайсов и есть объективные цифры. Да, детская болезнь. Лет ~20 назад столкнулись с такой проблемой. В наших приборах иногда пропадали сохраняемые данные. Пропадали именно в момент включения/выключения устройства. Схемотехнические переделки, вочтог, контроллеры питания почти сняли проблему. Полностью вылечили контролем достоверности данных — crc/хэш.


Никаких цифр у нас нет

хотелось бы добавить, что без добавления контрольных сумм цифр и не будет. про ту же zfs сколько писали про обнаружение silent data corruption на том железе, где этого раньше не замечали.


то же самое с ecc на серверах — в норме счётчики в нулях, но если есть проблемы, то видишь сразу цифры, понятно что происходит. без этого механизма просто видим "что-то глюкнуло" (а иногда и не видим, не все ошибки памяти приводят к сбоям).


это как секс с незнакомым человеком — только в презервативе — так и низкоуровневое хранение данных — только с контрольными суммами.

низкоуровневое хранение данных — только с контрольными суммами


Тогда и помехоустойчивое кодирование нужно тащить с собой.
Тогда и помехоустойчивое кодирование нужно тащить с собой.

А это уже от конкретной задачи зависит. Например, в CD-дисках есть большая вероятность повреждения информации, царапины на поверхности и пр., поэтому и кодируется информация на аппаратном уровне с использованием кодов Рида — Соломона, исправляющих ошибки, в несколько слоев, на фрейм и на блок.

в отличие от контрольных сумм, нельзя просто "добавить помехоустойчивое кодирование" — для выбора конкретного кодирования нужно понимать от ошибок какого рода мы защищаемся, какова вероятность этих ошибок и т.п.

Угу. Так какого рода ошибки возникают в EEPROM?

Затронута серьёзная проблема. Предложенное решение, на мой взгляд, не идеальное, но автор даёт хороший повод задуматься и возможность разрабатывать свою систему уже не на пустом месте.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории