tonyk_av 45 3 марта Опубликовано 3 марта · Жалоба 11 hours ago, jcxz said: Чтобы записать несколько байт, не нужно копировать сектора. Записывается может до 32К зараз. После записи может быть изменение нескольких байт. Вот где подвох. 11 hours ago, jcxz said: В том, что при неосторожном выключении питания в момент модификации данных, вся FS может слететь вместе с настройками? Такое может произойти при записи в любой флэш, хоть внутренний, хоть внешний. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 3 марта Опубликовано 3 марта · Жалоба 3 часа назад, tonyk_av сказал: Такое может произойти при записи в любой флэш, хоть внутренний, хоть внешний. Нет, если реализована работа минимум с двумя областями стирания/записи (секторами) с кольцевым способом дозаписи. О чем уже написал jcxz. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 3 марта Опубликовано 3 марта · Жалоба 15 часов назад, tonyk_av сказал: То есть нужно очистить сектор в 128К, скопировать туда сектор в 16К... Вообще не так все. Писать нужно в тот же (по возможности) сектор, но чистую область. Цитата Ну и ресурс у внешней флэш W25Qxx на порядок больше, чем у флэш МК, 100К против 10К. Открываю приведенный F446. Сектора 6 и 7 по 128К занимаем под эту самую "программу пользователя" в 32К, итого имеем 80К циклов стирания минимум. Уж если кому-то в голову придет 10 раз на день менять прошивку пользователя, то это ресурс на 8000 дней, а это почти 22 года безответственного насилования блока, который, уверен, к тому моменту потеряет срок гарантийных обязательств, а как максимум, истечет назначенный срок эксплуатации. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 3 марта Опубликовано 3 марта · Жалоба 6 часов назад, tonyk_av сказал: Такое может произойти при записи в любой флэш, хоть внутренний, хоть внешний. Если иметь голову на плечах и следовать правилу: "Никогда не писать поверх старых данных!" (только в новое место) - то никогда не произойдёт (до физического износа флеша конечно). Количество элементов стирания (секторов) должно быть >=2. Как уже писал выше Arlleex. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 45 3 марта Опубликовано 3 марта · Жалоба 3 hours ago, Arlleex said: Нет, если реализована работа минимум с двумя областями стирания/записи (секторами) с кольцевым способом дозаписи. Так у меня сейчас реализовано очень похоже, причём на разных физических носителях. Сначала всё пишется и изменяется в файле на внешней микросхеме флэш-памяти, а перед запускам прошивается во внутренний флэш МК. 18 hours ago, jcxz said: В чём "удобства" то? Не упомянул, что на контроллере есть ещё FTP-сервер, поэтому все настройки просто загружаются в контроллер по ftp. Кстати, ёмкость W25Q позволяет сохранять в контроллере весь проект с программой пользователя и схемами оборудования, где стоит контроллер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта · Жалоба Лет 20 назад я придумал интересную систему хранения настроек, и за все время я не потерял ни одного параметра. Конечно за это время она модернизировалась по C++11, флэшки с ECC и флэшки у которых стертое состояние является 0x00, но принцип остался неизменным. Система не занимает ни одного (заранее зарезервированного) байта ОЗУ, кроме стека; Масштабируемая, самовосстанавливающаяся, автоматическая; Работает на любых носителях (с которыми я сталкивался); Журналируемая (по возможности), можно поднять хронологию изменений; Относительно компактная; Щадящая ресурс носителя, увеличивая ресурс в десятки-сотни раз; Устойчивая даже к тому, что если залить совсем другую программу с другими настройками, и даже что-то в ней сохранить, а потом вернуть оригинальную прошивку, то вероятнее всего ничего не потеряется. Если комку интересно - могу описать Из недостатков: иногда может занять длительное время для дефрагментации и тем самым задержать ответ по какому нибудь протоколу. но это решается отложенной обработкой сохранения Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 63 4 марта Опубликовано 4 марта · Жалоба On 3/4/2024 at 10:45 AM, Ivan. said: Если комку интересно - могу описать Мне интересно Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта (изменено) · Жалоба Пожалуйста, только не надо трепа, а нафига и что за глупость. Последнюю версию выдать не могу, не потому, что жалко, а потому, что она писалась под специфический TMS контроллер и не оформлена (руки не доходят). Система разделена на 2 части: PropSaver - Занимается записью/чтением данных с виртуальной флэшки (это может быть даже файл в файловой системе) PropDesc - Формирует списки сохраняемых параметров для автоматизации процесса PropSaver //Компонента хранения параметров. //Компонента требует 2 области памяти для надежного хранения данных. //Памяти могут быть разного размера и различной реализации. //Хранение данных производится путем добавления нового блока данных в конец. //При инициализации, компонента проходит по всей памяти и находит конец данных по пустому идентификатору // //Каждый блок данных занимает 4 байта и имеет структуру: //Младший бит ----------- Старший бит //Data#### ######## ######## TpCrc### //Каждый блок данных содержит: //3 байта полезной нагрузки (Data); //2 бита флагов (Tp[0-удалена, 1-Идентификатор, 2-Значение]); //6 бит контрольной суммы (Crc) // //Каждая запись состоит из нескольких блоков данных: //1 блок данных с идентификатором записи //N блоков с данными //Т.е. для хранения 2 байт данных потребуется 8 байт данных: //Id###### ######## ######## 10Crc### //Value### ######## ........ 01Crc### //Для хранения большого объема данных блоки добавляются, например, для хранения 10 байт данных структура будет выглядеть следующим образом: //Id###### ######## ######## 10Crc### //Value### ######## ######## 01Crc### //Value### ######## ######## 01Crc### //Value### ######## ######## 01Crc### //Value### ........ ........ 01Crc### //При сохранении битовых полей данные сохраняются без уплотнения, например, при сохранении 5 бит данных начиная с 4 бита они разместятся следующим образом: //Id###### ######## ######## 10Crc### //....Valu e....... ........ 01Crc### //При чтении/сохранении битовых полей указатель должен быть указан на первый байт данных, а номер бита в диапазоне от 0 до 7 // //При стирании записи в блоке идентификатора стирается тип записи на 00 (это позволяет не перезаписывать всю страницу, а только дозаписать несколько бит данных). //Удаленная запись будет иметь вид: //Id###### ######## ######## 00Crc### //Value### ######## ........ 01Crc### //Даже после удаления данных можно проследить последовательность изменения, пока память не будет дефрагментирована. // //При загрузке параметра компоненте указывается идентификатор параметра и длинна данных в битах, которую необходимо загрузить. //Если цепочка блоков не позволяет загрузить все данные - загрузчик возвращает ошибку. //Благодаря идентификаторам - хранение параметров возможно в произвольном порядке. //Можно сохранять только те параметры, которые били изменены относительно значений по умолчанию, а остальные параметры загружать значениями по умолчанию. //При вводе новых параметров в новых версиях программы - старые параметры остаются доступными и не теряются. // //Последовательность сохранения данных: //1. Сперва производится добавление новой записи в первую память; //1а. Если в этот момент происходит сбой (сброс программы), то при загрузке программы данные будут загружены из второй памяти. //2. Производится стирание одной последней записи до только что записанной (дальнейший поиск совпадений не требуется); //2а. Если в этот момент происходит сбой, то после перезагрузки программы находит новые данные, удаляет все неудаленные блоки данных с таким же идентификатором, после чего дублирует запись во вторую память (тем самым завершает незаконченную процедуру сохранения). //3. Далее производит добавление новой записи во вторую память; //3а. Если в этот момент происходит сбой - компонента выполнит восстановление по пункту 2а. //4. Последним этапом компонента стирает во второй памяти один последний идентификатор до только что сохраненного; //4а. Если в этот момент произойдет сбой - компонента выполнит восстановление по пункту 2а. т.е. данные в первой памяти являются более актуальными. // //Последовательность загрузки данных: //1. Ищет первый с конца соответствующий идентификатор в первой памяти; //2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3. Производит загрузку данных; //3а. Если данные были загружены полностью: //3а1. Ищет такой же идентификатор с конца второй памяти; //3а2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3а3. Сверяет данные с теми, что были загружены из первой памяти; //3а3а. Если данные совпадают: //3а3а1. Возвращает положительный результат. //3а3б. Если данные отличаются: //3а3б1. Сохраняет данные во вторую память; //3а3б2. Возвращает положительный результат не зависимо от того удалось ли сохранить данные во вторую память. //3б. Если не удалось загрузить данные из первой памяти: //3б1. Ищет первый с конца соответствующий идентификатор во второй памяти; //3б2. Удаляет все недоудаленные такие же идентификаторы до текущей позиции; //3б3. Производит загрузку данных; //3б3а. Если данные были загружены полностью: //3б3а1. Сохраняет данные в первую память; //3б3а2. Возвращает положительный результат не зависимо от того удалось ли сохранить данные в первую память. //3б3б. Если не удалось загрузить данные из второй памяти: //3б3б1. Возвращает ошибку загрузки. // //Данные в каждой памяти хранятся асинхронно. //При заполнении одной из памяти она полностью стирается и производится полное копирование данных из резервной памяти в ту, которая только что была очищена. //Перед упаковкой одной памяти в другую компонента рассчитывает сколько места потребуется для добавления новой записи. //Если новая запись не влезает - компонента выполняет 1 из вариантов действий, который был указан при инициализации компоненты: //1. Возврат ошибки записи нового значения; //2. Удаление одной или нескольких самых старых записей, чтобы влезла новая (т.е. самые ранние сохраненные данные могут быть потеряны и при следующем запуске быть загруженными по умолчанию); //3. Удаление целой страницы данных наиболее давней записи; //4. Удаление половины памяти, но не менее одной страницы. В данном варианте блок данных занимает 4 байта, что не подходит для памяти с минимальным блоком данных 8 байт (не помню, по-моему у L4 такая память) В новой реализации я сделал блок данных по 8 байт, в которые входят: //Value### ######## ######## ######## Id###### ######## ######## 10Crc### //Value### ######## ######## ######## ######## ######## ######## 01Crc### Это позволило эффективнее хранить параметры. Первый блок имеет 3-х байтовый идентификатор и 4-х байтовую переменную Каждый последующий блок расширяет данные на еще 7 байт PropDesc //Система сохранения / загрузки списка параметров. //Данная компонента позволяет сформировать список всех полей данных для сохранения, автоматической загрузки всех параметров и восстановления значений по умолчанию при неудачной загрузке. //В список инициализации можно указывать любые поля данных, как глобальных переменных, так и отдельные поля объектов в любом порядке. //Можно указывать как целые структуры данных, так отдельные переменные вплоть до единичных битов. //Компонента позволяет хранить любые объекты размером от 1 бита до 8К байт (в 16 битных системах) и до 512М байт (в 32 битных системах). //Также компонента позволяет хранить массивы данных от 0 до 255 элементов. //Компонента самостоятельно создает идентификатор переменной на основе хеш суммы строкового представления имени параметра. //То есть при указании переменной, которую требуется сохранить компонента преобразует имя переменной в строку, вычислит трехбайтную хеш сумму этой строки и сохранит данные под данным идентификатором. //Это позволяет формировать список в любом порядке, пересортировывать его и вводить новые параметры по мере их появления. //Идентификаторы формируются на этапе компиляции и не расходуют программные ресурсы. Также весь список может быть сохранен в кодовую часть памяти не расходуя оперативную память. //Трехбайтный идентификатор получается своего рода случайным числом. //Вероятность совпадения идентификаторов у разных переменных является достаточно малой для небольших списков порядка 1000 элементов. //Для проверки совпадений идентификаторов и наложения областей памяти существует функция checkOverlap(). //Данную проверку можно выполнять единоразово, перед выпуске релиза и потом исключить из основной программы. //Для восстановления недозагруженных параметров необходимо указать область константной памяти с копией объекта заполненного значениями по умолчанию. //Для эмбеддед архитектуры нет необходимости указывать дефолтный объект, т.к. компонента самостоятельно найдет его в слепке памяти инициализации оперативной памяти. //Если дефолтный объект не указан, или для эмбеддед архитектуры объект находится в области noinit или init0, то при необходимости восстановления он заполняется 0. // //Пример создания списка параметров (в частности пример приведен для эмбеддед архитектуры, где не нужно указывать значения по умолчанию: //Допустим у нас есть некие переменные и объекты: //int16_t var1 = 10; //Единичная переменная имеет значение по умолчанию 10. //int16_t var2[5] = {1, 2, 3, 4, 5}; //Массив переменных из 5 элементов. //int16_t var3; //Переменная var3 не проинициализрована и имеет значение по умолчанию 0. //char str1[10] = "str1"; //Строка из 9 символов и 0 терминатором в конце. //struct Obj { //Структура данных в которой все поля данных проинициализированы значениями по умолчанию. // int16_t field1 = 1; // int8_t field2 = 2; // union { // uint16_t bitFields_1_2 = 0; //Вспомогательное поле для удобства взятия указателя на битовые поля, т.к. данные в структуре могут быть выравнены согласно архитекруте. // struct { // uint16_t bitField1 :5; //Битовое поле размером 5 бит. Битовые поля нельзя инициализировать значениями по умолчанию в месте их объявления. // uint16_t bitField2 :11; //Для этого можно их инициализировать во вспомогательной переменной или constexpr конструкторе. // }; // }; // union { // uint8_t bitArray_4_2 = 0; //Вспомогательное поле для массива битовых полей из 2 элементов по 4 бита каждый. // struct { //Нельзя создавать битовые массивы, для этого представим массив из независимых битовых полей. // uint8_t bitArrayElement0 :4; // uint8_t bitArrayElement1 :4; // }; // }; //} obj1, obj2; //Создадим 2 объекта данной структуры. // //Инициализация списка сохраняемых параметров: //DESCLIST(PropDescList1, //Имя типа списка инициализации. // VARDESC(var1); //Элемент списка, указывающий на переменную var1. хеш сумма вычисляется из строки "var1". // //В случае невозможности загрузки, переменная будет проинициализирована значением 10. // VARDESC(var2); //Элемент списка, указывающий на массив переменных var2. Каждый элемент массива будет сохранена под своим идентификатором. // //Первый элемент будет сохранен под идентификатором "var2.0", второй под "var2.1" и т.д. // VARDESC(var3); //Переменная var3. В случае неудачной загрузки будет заполнена 0 т.к. она не была помещена в область инициализированных переменных. // VARDESC(str1); //Переменная str1 будет сохраняться единым объектом размером 10 байт, а не массивом из 10 переменных т.к. для строковых параметров предусмотрена специальная реализация. // VARDESC(obj1.field1); //Элемент списка указывающий на поле field1 объекта obj1. Переменная будет сохранена под идентификатором "obj1.field1". // BITDESC("obj1.bitField1", &obj1.bitFields_1_2, 0, 5); //Битовое поле bitField1 будет сохранено под идентификатором "obj1.bitField1", которое формируется не на основе имени переменной, а указывается вручную первым аргументом. // //Битовое поле будет размещаться возле переменной obj1.bitFields_1_2 с 0 бита размером 5 бит. // BITDESC("obj1.bitField2", &obj1.bitFields_1_2, 5, 11); //Битовое поле bitField2 будет сохранено под идентификатором "obj1.bitField2". // //Битовое поле будет размещаться возле переменной obj1.bitFields_1_2 с 5 бита размером 11 бит. // BITDESC("obj1.bitArray1", &obj1.bitArray_4_2, 0, 4, 2); //Указатель на массив битовых полей расположенных возле переменной obj1.bitArray_4_2 начиная с 0 бита размером каждого элемента в 4 бита и в количестве 2 элементов. // VARDESC(obj2); //Объект obj2 будет загружаться и сохранятся полностью при изменении любого поля объекта. // VARDESC(obj2.field2); //Элемент указывающий на obj2.field2 бессмысленен, т.к. он ни когда не будет сохранен отдельно, а будет сохраняться целой структурой описанной предыдущей строкой. //); // //Создание списка сохраняемых параметров. //const PropDescList1 propDescList1; //Переменная автоматически разместиться в кодовой памяти для AVR архитектуры, т.к. к структуре применен атрибут PROGMEM. // //Загрузка всех параметров: //propDescList1.load(propSaver1); //компонента загрузит все указанные в списке переменные и восстановит их при необходимости. // //Сохранение отдельных переменных: //propDescList1.save(propSaver1, &obj1.field2); //Компонента найдет какому из элементов списка принадлежит данная память и сохранит ее с помощью объекта PropSaver. //propDescList1.save(propSaver1, &obj2.bitField_1_2, 8); //Компонента найдет какому из элементов списка принадлежит данная память и сохранит ее. // //Данные по адресу obj2.bitField_1_2 начиная с 8 бита принадлежат полю obj2.bitField2 начиная с 5 бита размером 11 бит // //и могли бы быть сохранены отдельно для этой переменной в случае с объектом obj1, // //но объект obj2 описан для сохранения целиком и будет сохранен полностью всем объектом. Изменено 4 марта пользователем Ivan. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта (изменено) · Жалоба Вся компиляция укладывается примерно в 3кБ (на STM32) Вместо PropDesc у меня есть более сложный компонент: ValueDesc. Это уже глобальная структура всех переменных в системе, к которой могут ссылаться протоколы связи, графические меню, хранители параметров, журналы событий. Имеет такую структуру: //3 2 1 0 //Caption# ######## ######## ######## - Caption: Заголовок //Ptr##### ######## ######## ######## - Ptr: Указатель на переменную (объект) //Array### Hash#### ######## ######## - Array: Массив, Hash: Идентификатор //Для переменной //1SEacBit Limits## Bits#### ######## - S: Сохраняемый, Eac: Уровень доступа для изменения, Bit: Стартовый бит, Limits: Количество лимитов, Bits: Размер в битах //Для объекта //0Size### ######## ######## ######## - Size: Размер в байтах // //Viewtype ######## ######## ######## - Viewtype: Указатель на тип отображения (на ValueDescList для объекта) //[ - Лимиты //Bits#Bit MAcAdd## ######## ######## - Bits: Размер переменной в битах, Bit: Стартовый бит, M: Максимальное ограничение, Ac: Максимальный уровень ограничения, Add: Коррекция лимита (не действует, если константа) //Lim##### ######## ######## ######## - Lim: Указатель на переменную ограничения (Константа, если Bit==7, Bits==31) //] Эта структура работает с той же PropSaver, но имеет древовидную систему, проверяет валидность загруженных данных (минимальное, максимальное значение) может иметь сложные зависимости лимитов с гистерезисом, например: var1 не может быть больше var2 - 10. И все это конечно формируется на этапе компиляции, т.е. не занимает ни одного байта кодовой памяти, кроме самой таблицы Изменено 4 марта пользователем Ivan. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта (изменено) · Жалоба Описание структуры переменных имеет примерно такой вид: VALUELIST(ValueDescList, // AC, NAME, VALUE, CAPTION, VIEWTYPE, LIMITS... VALUE_DESC(vdNoSaved, currentValue, currentValue, "currentValue", VTDec::vt<3>(), {lacAbsoluteMin, 0_mA}, {lacAbsoluteMax, 24_mA}); VALUE_DESC(vdSavedService, calib4mA, calib4mA, "calib4mA", VTDec::vt<3>(), {lacAbsoluteMin, 2_mA}, {lacAbsoluteMax, 8_mA}); VALUE_DESC(vdSavedService, calib20mA, calib20mA, "calib20mA", VTDec::vt<3>(), {lacAbsoluteMin, 16_mA}, {lacAbsoluteMax, 30_mA}); ... // NAME, OBJ, CAPTION OBJ_DESC(hartParserSlave, hartParserSlave, "Hart HartParserSlave"); OBJ_DESC(variables, variables, "Hart Variables"); OBJ_DESC(identifier, identifier, "Hart Identifier"); ); extern const ValueDescList valueDescList; Изменено 4 марта пользователем Ivan. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 63 4 марта Опубликовано 4 марта · Жалоба On 3/4/2024 at 11:52 AM, Ivan. said: Пожалуйста, только не надо трепа, а нафига и что за глупость. Спасибо ! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 4 марта Опубликовано 4 марта · Жалоба 3 часа назад, Ivan. сказал: Система не занимает ни одного (заранее зарезервированного) байта ОЗУ, кроме стека; Масштабируемая, самовосстанавливающаяся, автоматическая; Работает на любых носителях (с которыми я сталкивался); Журналируемая (по возможности), можно поднять хронологию изменений; Относительно компактная; Щадящая ресурс носителя, увеличивая ресурс в десятки-сотни раз; Устойчивая даже к тому, что если залить совсем другую программу с другими настройками, и даже что-то в ней сохранить, а потом вернуть оригинальную прошивку, то вероятнее всего ничего не потеряется. Система хранения, базирующаяся на обычном кольце секторов, умеет всё то же самое + не требует никакой дефрагментации. И не требует "2 памятей". Нужна только одна цепочка секторов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта · Жалоба 1 минуту назад, jcxz сказал: Система хранения, базирующаяся на обычном кольце секторов, умеет всё то же самое + не требует никакой фрагментации. И сколько секторов она занимает? Если Вам потребуется сохранить новую переменную, Вы ее будете писать в следующий сектор или стирать и писать заново в один единственный сектор? В моем случае это по одному-два сектора на каждом носителе. Если стирать - то ее нужно где-то временно хранить. Допустим - это вторая копия, тогда Вы будете каждый раз ее копировать из одной в другую флэшку? А если флэшки разного размера? одна 16к, а втора EEPROM на 2к? Даже просто перезаписать целую страницу - уйдет в десятки раз больше времени, чем дописать новы блок в 8 байт. А ресурс? самое страшное для памяти - это не запись, а стирание страницы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Ivan. 4 4 марта Опубликовано 4 марта · Жалоба 28 минут назад, jcxz сказал: И не требует "2 памятей". Нужна только одна цепочка секторов. И здесь не обязательно 2 независимых носителя. можно взять 2 сектора на одном носителе 31 минуту назад, jcxz сказал: Система хранения, базирующаяся на обычном кольце секторов, умеет всё то же самое А можно краткое описание? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 4 марта Опубликовано 4 марта · Жалоба 15 минут назад, Ivan. сказал: И сколько секторов она занимает? Сколько нужно пользователю: от 2-х до всех секторов носителя. 15 минут назад, Ivan. сказал: Если Вам потребуется сохранить новую переменную, Вы ее будете писать в следующий сектор или стирать и писать заново в один единственный сектор? В систему хранения сохраняется структура настроек целиком, как единый блок. Не пилю её на множество переменных. Хотя можно и так сделать, при необходимости. На носителе структура занимает почти столько байт, сколько весит сама (плюс небольшой оверхед = CRC + ещё неск. байт). 15 минут назад, Ivan. сказал: В моем случае это по одному-два сектора на каждом носителе. На каждую переменную??? 15 минут назад, Ivan. сказал: Если стирать - то ее нужно где-то временно хранить. Допустим - это вторая копия, тогда Вы будете каждый раз ее копировать из одной в другую флэшку? А если флэшки разного размера? одна 16к, а втора EEPROM на 2к? Ещё раз: Кольцу нужен только один носитель. Никаких "двух памятей" не нужно. И никакого "временно хранить" не нужно. Зачем? В кольце всегда есть предыдущая копия структуры (и даже скорей всего не одна, а множество). Которая и будет прочитана если во время сохранения новой структуры произойдёт сбой питания. 15 минут назад, Ivan. сказал: А ресурс? самое страшное для памяти - это не запись, а стирание страницы. Ресурс тоже определяется пользователем: Чем больше секторов он выделит для кольца, тем больше будет мультипликатор ресурса. 8 минут назад, Ivan. сказал: А можно краткое описание? Я уже описывал здесь на форуме ранее. Поищите. И не кратко, а развёрнуто. Поищите. PS: Вот оно: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться