Перейти к содержанию
    

Как зарезервировать страницу памяти в середине кода

1 минуту назад, jcxz сказал:

В систему хранения сохраняется структура настроек целиком, как единый блок

а завтра Вам потребовалось добавить переменную в структуру? хана прошлым настройкам? или вы будете добавлять идентификатор версии настроек? и в каждом новом релизе уметь загружать каждую предыдущую версию настроек?
В чем же ее масштабируемость?

 

3 минуты назад, jcxz сказал:

На каждую переменную???

На все переменные. Каждая переменная занимает в среднем по 8 байт *2 копии

5 минут назад, jcxz сказал:

Ещё раз: Кольцу нужен только один носитель. Никаких "двух памятей" не нужно

Не нужно или невозможно?

 

6 минут назад, jcxz сказал:

Ресурс тоже определяется пользователем: Чем больше секторов он выделит для кольца, тем больше будет мультипликатор ресурса.

А жирно не будет? Вот к примеру F407: 4 сектора по 16к, а дальше по 128к*. И сколько секторов Вы выделите под настройки? Полагаю максимум 2. И при каждом изменении Вы будете стирать один из двух секторов.
А как определить какая из двух копий последняя? счетчик вводить, кто старше, тот и папа?
В моем случае 16к мне хватит на 2048 изменений (стандартных параметров по 4 байта)
Еще в моем случае количество настроек может быть больше, чем доступный объем носителя. Сохраняются только те настройки, которые были изменены.
Допустим у меня есть 50 дискретных входов, каждый из которых можно проинвертировать, сымирировать или перенаправить куда нибудь. Все эти параметры одновременно ни кто настраивать не будет, но возможность изменить и сохранить каждый из них - в моей системе есть. И это не фантазии, у меня реально были такие проекты, где настроек на порядок больше, чем EEPROM в ATMega.

 

19 минут назад, jcxz сказал:

Не пилю её на множество переменных. Хотя можно и так сделать, при необходимости.

Интересно как? Возможно Вы не создавали проекты с тысячами переменных. Калибровку одного температурного сенсора можно и так хранить.

24 минуты назад, jcxz сказал:

Я уже описывал здесь на форуме ранее. Поищите. И не кратко, а развёрнуто. Поищите.

Обязательно почитаю

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Ivan. сказал:

а завтра Вам потребовалось добавить переменную в структуру? хана прошлым настройкам? или вы будете добавлять идентификатор версии настроек? и в каждом новом релизе уметь загружать каждую предыдущую версию настроек?
В чем же ее масштабируемость?

Да - инкрементирую номер версии и храню набор трансляторов номеров версий структур, начиная от самой старой. В чём проблема? Вы каждый день меняете формат структуры? Если так - то у вас проблемы с планированием разработки ПО.  :unknw:

Если же уметь планировать, то таких изменений бывает всего максимум несколько раз в год. И нет никакой проблемы хранить пару десятков трансляторов. Они все лёгкие и простые. Транслятор вызывается однократно. Зато потом загрузка структуры настроек очень быстрая, так как не нужно лазить и собирать ключи настроек по мелким кусочкам из разных мест памяти. И нет проблемы, что при очередном добавлении нового ключа настроек, он станет несовместим с другим ключом, хранимым где-то уже.

1 час назад, Ivan. сказал:

Не нужно или невозможно?

Я вроде ясно написал: не нужно.

Зачем?

1 час назад, Ivan. сказал:

А жирно не будет? Вот к примеру F407: 4 сектора по 16к, а дальше по 128к*. И сколько секторов Вы выделите под настройки? Полагаю максимум 2. И при каждом изменении Вы будете стирать один из двух секторов.

Вы вроде просили описание моего формата. Но так его и не прочитали. Зачем тогда просили??

В моём алгоритме нет стирания "при каждом изменении". Очередной сектор стирается только при полном заполнении предыдущего. Прочитайте наконец то, что сами просили.

К тому же - устойчивость к сбоям питания никак не обеспечить на одном секторе. Какую бы чудесную систему хранения вы не придумали. Так что - вашей тоже потребуется не менее двух. И к тому же как я понял - у вас хранение того же объёма данных займёт кратно больше памяти. Так что вопрос "не жирно?" - отнесите к себе.

1 час назад, Ivan. сказал:

А как определить какая из двух копий последняя? счетчик вводить, кто старше, тот и папа?

Прочитайте наконец описание, которое просили! Там всё написано.

1 час назад, Ivan. сказал:

Интересно как? Возможно Вы не создавали проекты с тысячами переменных. Калибровку одного температурного сенсора можно и так хранить.

Точно так же как у вас - снабдить каждый ключ конфигурации уникальным идентификатором и сохранить его в кольцевом буфере.

Формат структуры настроек никак не относится к системе хранения настроек во флешь. Это две разных сущности. Я писал о хранении настроек в кольцевом буфере. Как простом и надёжном средстве хранения. Устойчивом к сбоям питания во время операций записи и стирания. И с коротким временем старта. И способной стартовать с произвольного содержимого флешь (флешь может быть изначально заполнена произвольным мусором), начальная инициализация производится автоматически.

А что именно там хранить - одну единую структуру настроек программы или кучу отдельных ключей с идентификаторами - без разницы. Можно хранить и то и то, в том числе и смешивая их.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

36 минут назад, jcxz сказал:

Прочитайте наконец описание, которое просили! Там всё написано.

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

Универсальный журнал.
1. ведение циклического журнала различного вида событий;
2. сквозная нумерация события, т.е. при переполнении журнала начальный индекс должен смещаться по мере его затирания;
3. журнал должен быть компактным (упакованным);
4. журнал должен быть самодостаточным, чтобы можно было считать журнал с любого устройства не имея каких то вспомогательных таблиц данных. таких как: номер события 1 - означает перезагрузку устройства;
5. каждое событие должно иметь возможность вставки переменных. например: изменение параметра такого с # на # значение. или сработка при температуре 90°С;
6. подходить как к большим флешкам, так и к маленьким и очень быстро с ними работать;
7. щадить флешку, равномерно используя функцию записи по всему пространству;
8. ну и быть достаточно надежным, чтобы не потерять данные.
Для упаковки журнала вместо текста события применяется кодовый идентификатор - типа, код 1 означает перезагрузку устройства.
Для этого каждое устройство может передать ту самую таблицу соответствия кодов в текст, чтобы внешняя программа могла сперва считать таблицу кодов, а потом вычитать журнал заменив коды текстами.
каждое событие описывается формат строкой по типу printf.
например:
Изменение параметра \"Адрес устройства\" с #u на #u
каждое событие хранит в себе идентификатор события и набор бинарных данных, которые нужно подставить вместо # кодов.
формат строка была модифицирована по сравнению с printf под свои задачи. теперь существуют следующие типы значений:
#i, #I, #j, #J - вывод знакового целочисленного числа с 1, 2, 4 и 8 байт соответственно;
#u, #U, #l, #L - вывод беззнакового целочисленного числа с 1, 2, 4 и 8 байт соответственно;
#x, #X, #h, #H - шестнадцатеричное число 1, 2, 4 и 8 байт соответственно;
#f, #F - число с плавающей точкой 4 и 8 байт соответственно;
Каждый числовой параметр можно модифицировать дополнительными инструкциями, например:
#.1i - означает, что целочисленное число должно быть представлено в виде десятичной дроби с 1 знаком после запятой;
#/10i - означает, что целочисленное число должно быть поделено на 10;
#4X - означает, что число должно быть представлено в шестнадцатеричной системе длинной минимум 4 символа.
данные модификации можно комбинировать, например:
#.1/10i - поделить на 10 и вывести с одним знаком после запятой.
#d - формат даты и времени из 4 байт данных (количество секунд с 2000 года);
#c, #C - символ ASCII и UTF-8 соответственно;
#s, #S - строка ASCII и UTF-8 соответственно;
#e, #E - перечисляемый тип.
для перечисляемого типа необходимо перечислить все варианты перечисления:
#```None``Even``Odd`e
первые 2 кавычки означают, что значение 0 отсутствует
если в событии существуют 2 повторяющихся перечисления то их можно упростить:
Изменение параметра \"Бит четности интерфейса\" с \"#```None``Even``Odd`e\" на \"#e\"
второе значение #e означает, что оно отображается как предыдущее.
Также существуют базовые типы отображения:
#a, #A - логический (Inactive/Active)(Неактивный/Активный)
#b, #B - логический (False/True)(Нет/Да)
#o, #O - логический (Off/On)(Выкл/Вкл)
это была формат строка. Теперь к самому журналированию:
Допустим у нас есть флешка на 10 МБ. запись идет циклически. задача заключается в том, что при старте программы необходимо определить начало и конец журнала, которые находятся где-то в середине флешки.
можно записывать в определенное место счетчик. но тогда данная часть флешки быстро выйдет из строя.
количество записей в одну страницу возможно в районе 10 - 100 т. раз. точнее не запись, а количество стираний страницы памяти ограничено.
Забегая немного вперед - опишу одну единицу записи:
Каждая запись хранить следующую информацию:
- Порядковый номер события;
- Время возникновения события;
- Идентификатор события;
- Тип события;
- Вспомогательные данных;
- Проверка валидности записи.
Сколько событий в системе может быть? - ну, штук 10, 100, может даже 1000.
Можно расставить их по порядку и добавлять порядковый номер по мере их появления в программе. правда появится такая путаница, возможность появления двух событий под одинаковыми номерами. невозможность сортировки по группам.
можно вообще применить строковый идентификатор, например: "restart" - но это расточительно.
В данном журнале применяется строковый идентификатор сжатый до 3 байт кода путем подсчета hash суммы строкового представления имени события.
правда тут возникает вероятность совпадения hash суммы разных событий, но данную проблему можно решить путем проверки совпадений кодов событий один раз при первом старте программы или при выдаче очередного релиза программы.

Итак. Теперь упакуем данные в одну запись:
Одна запись будет состоять из 8 байт:
Байты данных:
7. Crc##0Tp
6. Event###
5. ########
4. ########
3. DateTime
2. ########
1. ########
0. ########
4 байта времени события
3 байта идентификатора события
2 бита типа события (0: Info, 1: Warning, 2: Error, 3: Change)
1 бит служебный (0 - указывает, что это событие, 1 - прочие записи)
5 бит CRC записи
Другой тип записи. дополнительные данные к событию:
7. Crc##100
6. Data####
5. ########
4. ########
3. ########
2. ########
1. ########
0. ########
В данных записях нигде нет порядкового номера события. я решил не впихивать его в каждое событие, а периодически помечать между событиями следующей записью:
7. Crc##111
6. 00000000
5. 00000000
4. 00000000
3. Index###
2. ########
1. ########
0. ########
Пустая запись в которой еще никогда ничего не было записано:
7. 11111111
6. 11111111
5. 11111111
4. 11111111
3. 11111111
2. 11111111
1. 11111111
0. 11111111
так как большинство флешек позволяют писать любой объем данных*, а стирать только постранично - писать в пустую страницу можно не стирая ее предварительно. т.е. мы будем дозаписывать каждое событие не стирая страницу, а стирать только тогда, когда будем производить первую запись в странице.
таким образом мы можем писать в страницу памяти гораздо больше раз, чем ее ограничение. ведь ограничение распространяется только на количество стираний страницы.

Вернемся к поиску начала и конца журнала.
т.е. условные 10 МБ памяти прочитать и пропарсить - это очень длительная операция - я решил разделить память на блоки. например флешку на 20 МБ делим на 20 частей. получается 1 блок = 1 МБ. этот объем может быть практически любым 1 КБ или 100 МБ в зависимости от общего объема.
В начале каждого такого блока мы записываем запись со стартовым индексом события в данном блоке, а во всем остальном пространстве блока сами события и данные к ним.
при переходе на следующий блок снова ставим метку стартового индекса в данном блоке.
как только вся память будет заполнена - мы стираем сразу блок в 1 МБ.
Конечно это расточительно, но необходимо для быстрого поиска начала и конца журнала. данный блок по логике программы будет составлять от 1/16 до 1/31 части всей флешки (в зависимости от кратности самой флешки).
Теперь при старте программы, компонента журнала пробегает по всем блокам и проверяя в них только первую запись - находит максимальный индекс события.
найдя примерный хвост журнала ищем начало журнала. теперь шагаем назад и ищем минимальный индекс блока, который четко совпадает с расчетным индексом. т.е. предыдущий блок должен иметь стартовый индекс равный текущему - размер блока деленный на размер записи.
если стартовая запись в блоке испорченная или соответствует ожиданиям - переходим к предыдущей. тем самым мы находим начало журнала, даже если часть данных в них испорчена (что тоже допускается, главное минимизировать вероятность потери всего журнала).
Ну и третий шаг - это найти точное положение хвоста журнала внутри блока в 1 МБ.
тут пойдем другим путем и методом половинного деления найдем пустую запись. Методом половинного деления мы находим пустую запись всего за 17 чтений записи для размера блока в 1 МБ.
ИТОГ: всего за ~80 небольших чтений мы полностью определяем границы журнала для флешки в 32 МБ. для флешки в 1 ГБ - это будет примерно 90 итераций. это займет несколько миллисекунд.
Вот так в общем выглядит журнал событий.
остается только одна проблема - как очистить целый блок размером в 1 МБ при переходе на очередной блок? это очень долго стирать целый мегабайт.
тут система еще хитрее. стираем мы не весь блок, а только те страницы, которые нам потребуются при поиске пустой записи методом половинного деления. т.е. такой обратный метод половинного деления.
таким образом при переходе к очередной странице стирания программа сотрет выборочные страницы количество которых не больше 5 - 10 страниц даже для очень больших флешек равномерно размазав время стирания блока по ходу заполнения журнала.
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, журнал тоже можно хранить. Без разницы. Это же система хранения записей в кольцевом буфере. Любых записей.

11 часов назад, Ivan. сказал:

условные 10 МБ памяти прочитать и пропарсить - это очень длительная операция

Не знаю - что значит в вашем понимании "очень длительная операция". Вот у меня в одном проекте система хранения занимает 4 сектора по 256К. Dual-SPI SCLK=30МГц. При таких параметрах, длительность запуска системы хранения = около 30-40 мсек. Это много?

Да, используется всего 1МБ. Но если систему хранения распространить на весь объём флешки (64 МБ), то время старта станет примерно ~60мсек. Хотя это совершенно не нужно, так как даже 4 сектора обеспечивают такой ресурс по перезаписи настроек, что заведомо хватит при любом использовании устройства.

11 часов назад, Ivan. сказал:

Ладно, Ваша система очень хороша, и моя заслуживает внимания. :drinks:

Я тоже ранее создавал системы хранения с разбивкой на кластеры, счётчиками ресурса секторов и т.п. Но имхо - всё это излишне для простой задачи хранения/модификации настроек в типичном устройстве на МК. Где настроек не мегабайты. Для этого вполне достаточно простого и надёжного кольцевого буфера.

11 часов назад, Ivan. сказал:

таким образом при переходе к очередной странице стирания программа сотрет выборочные страницы количество которых не больше 5 - 10 страниц даже для очень больших флешек равномерно размазав время стирания блока по ходу заполнения журнала.

Не понимаю - о каких флешках вы тут говорите. Обычно в терминологии разных SPI-флешек, с которыми я работал, было "страница" - это минимальный элемент записи; "сектор" (или "блок") - минимальный элемент стирания. Сектора/блоки всегда намного крупнее страниц.

Единственная система флешек, в которых единицы стирания == единицам записи - это серия DataFlash AT45 (от бывшего Atmel). Но и там - выгоднее стирать не страницами (хоть и позволяют), а секторами. Так как это значительно быстрее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

 У меня были системы с журналированием событий на MMC, а это минимум 4ГБ.

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

Кстати, Вы писали, что в Вашей системе можно тоже сохранять несколько структур:

15 часов назад, jcxz сказал:

В систему хранения сохраняется структура настроек целиком, как единый блок. Не пилю её на множество переменных. Хотя можно и так сделать, при необходимости.

Т.е. у Вас может быть 2 разных по размеру и наполнению структуры? Допустим сохранили одну структуру, а потом 10 раз вторую и нужно стирать первый сектор? Вы потеряете первую структуру?
 

13 часов назад, jcxz сказал:

К тому же - устойчивость к сбоям питания никак не обеспечить на одном секторе

По одному сектору на каждом носителе (или 2 сектора на одном носителе). Все надежно.

 

13 часов назад, jcxz сказал:

И к тому же как я понял - у вас хранение того же объёма данных займёт кратно больше памяти.

У меня 2 копии, а у Вас, как я понял, "скорее всего множество" + целый сектор маркера:

15 часов назад, jcxz сказал:

В кольце всегда есть предыдущая копия структуры (и даже скорей всего не одна, а множество)

 

В общем это разные системы. хватит их сравнивать

18 часов назад, Ivan. сказал:

Пожалуйста, только не надо трепа, а нафига и что за глупость.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 часов назад, Ivan. сказал:

 У меня были системы с журналированием событий на MMC, а это минимум 4ГБ.

Разве MMC/SD позволяет дописывать данные в страницу?

В теме ведь шла речь именно о таких носителях. И MMC/SD сюда вроде не относятся.

11 часов назад, Ivan. сказал:

У Вас для профессионалов, умеющих планировать количество настроек. У меня для требовательных потребителей, которые каждый месяц просят чтото добавить.
У Вас для хранения единой структурой. У меня для хранения множества меленьких переменных.

Вы всё время смешиваете тёплое с мягким.  :unknw:

Ещё раз повторю: Алгоритм хранения объекта (структуры) данных на носителе и внутренний формат этого объекта - это вещи совершенно несвязанные!

Какая разница - как внутри устроен этот объект? Находится внутри него просто двоичная структура данных, или внутри он из себя представляет список связанных пар:

имя_ключа=значение_ключа.

Мой алгоритм описывает именно алгоритм хранения на носителе. Организации расположения и порядка работы с данными на носителе. А как выглядит внутри сохраняемый объект данных - моему алгоритму абсолютно фиолетово. Если нравится хранить настройки в виде списка пар имя_ключа=значение_ключа, то нет никакой проблемы сохранить в моём алгоритме такой объект. Или записать отдельными записями - по одной записи на каждую пару. Просто будет дольше считывать.

Мой алгоритм совершенно не привязан к внутреннему формату сохраняемого объекта данных. Есть только требование возможности валидации объекта (проверки на валидность содержимого) и всё.

11 часов назад, Ivan. сказал:

Т.е. у Вас может быть 2 разных по размеру и наполнению структуры? Допустим сохранили одну структуру, а потом 10 раз вторую и нужно стирать первый сектор? Вы потеряете первую структуру?

Когда будет затёрт хвост сохранённых ранее объектов - определяется выделенным количеством секторов флеши. Выделите больше секторов и получите более длинную историю. Как в любом кольцевом буфере. При 4 секторах (по 256KB) в кольце и размере сохраняемых объектов ~1КБ байт размер истории будет колебаться примерно от ~512 до ~768 объектов. Мало? Значит выделите больше секторов. В наше время объём флешевых чипов в несколько МБ - вроде как не проблема.

Увеличение кол-ва секторов в кольце также увеличивает мультипликатор количества перезаписей. Только ради этого можно не жаться и отдать кольцу больше секторов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 часов назад, jcxz сказал:

Разве MMC/SD позволяет дописывать данные в страницу?

позволяют, вплоть до 1 бита

 

Спасибо всем, кто помог решить первоначальную задачу

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 3/4/2024 at 3:16 PM, Ivan. said:

И сколько секторов Вы выделите под настройки? Полагаю максимум 2. И при каждом изменении Вы будете стирать один из двух секторов.

ложное предположэніе. 

при изменении параметра я допишу его в пустое место текущего сектора

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...