Я занимаюсь разработкой автономных систем управления и анализа для тяжелой промышленности. Использую язык C (C99-C11). Очень беспокоит вопрос повреждения файлов и файловых систем, использующихся в Windows (Embedded, CE и пр.) при внезапном обесточивании или при жестком прерывании рабочего процесса/потока, например из-за исключения.
Хотелось бы разобраться в этом вопросе, достойной литературы по этой теме найти не удалось. Посоветуйте что-нибудь, желательно не на английском языке. Или, может быть, кто-нибудь сможет упрощенно объяснить следующее:
Ясное дело, если мы открыли файл и пишем в него, и в этот момент происходит отключение питание, или возникает фатальное исключение в одном из потоков процесса, то операция прерывается на полпути. Это практически гарантированно приводит к повреждению файла/файловой системы в том районе, где происходит работа.
Но ведь даже если мы не работаем с файлом, а в этот момент отключается питание, файл может быть поврежден из-за того, что ОС в фоновом режиме занимается дефрагментацией/индексацией. Это так?
Есть ли гарантия, что после возврата управления в программу после функций fclose() или fwrite() данные гарантированно корректно занесены на носитель?
Например, ситуация:
Я предполагал защитить файл критической секцией, и использовать эту же критическую секцию для инициации аварийного завершения. Тогда, предположительно, после того как работа с файлом закончится, критическая секция освободится, и второй поток сможет в нее зайти и вызвать abort() или аналогичную функцию планируемого жесткого прерывания процесса.
Проблема на мой взгляд в том, что даже после того, как мы сделали fopen() + fwrite() + fclose(), нет гарантий, что файл корректно сохранен, а не гуляет где-то в буферах, которые связаны с нашим процессом.
И как контролируется процесс переноса данных из внутреннего промежуточного буфера-кэша жесткого диска на сам носитель?
Например, файл записан и закрыт, и даже процесс, который все это делал, уже закрыт. Но данные частично или полностью еще находятся во внутреннем буфере-кэше накопителя. Если в этот момент произойдет обрыв питания, то данные, я почти уверен, будут повреждены или уничтожены.
Как ОС отслеживает перед своим завершением факт того, что данные из буфера накопителя записаны окончательно?
Но ведь даже если мы не работаем с файлом, а в этот момент отключается питание, файл может быть поврежден из-за того, что ОС в фоновом режиме занимается дефрагментацией/индексацией. Это так?
Во-первых, сомневаюсь, что драйвер самостоятельно будет дефрагментировать что-либо (хотя и не буду утверждать обратное), а отсутствие насущности проблемы фрагментации объясняется в первую очередь грамотными алгоритмами выбора блоков для хранения файлов.
А критические, неустранимые ошибки ФС в таких случаях — это практически невероятный сценарий (скажем, внезапный отказ HDD — куда более вероятен). Но повреждения данных (файлов) с которыми велась активная работа в момент отказа — штатная ситуация.
2) Есть ли гарантия, что после возврата управления в программу после функций fclose() или fwrite() данные гарантированно корректно занесены на носитель?
Нет.
fwrite () гарантирует только занесение данных в пользовательский буфер, но не гарантирует, что эти данные будут переданы ОС. Чтобы принудительно сбросить пользовательский буфер есть функция fflush (). При скоропостижной кончине процесса данные могут быть потеряны.fclose () и fflush () сбрасывают пользовательский буфер, но не гарантируют, что ОС передаст данные на носитель. Если процесс умрёт после их возврата, то изменения не потеряются, но они могут быть потеряны в случае внезапного отключения питания.3) И как контролируется процесс переноса данных из внутреннего промежуточного буфера-кэша жесткого диска на сам носитель?
fsync (). Он гарантирует, что вернёт управление (само собой без ошибки) только после того как данные физически попадут на диск и переживут внезапный отказ системы. В Win API аналогом FlushFileBuffers (), согласно документации он сбрасывает системные буферы, но, гарантирует ли сброс буферов самого HDD или нет, я не знаю.Как ОС отслеживает перед своим завершением факт того, что данные из буфера накопителя записаны окончательно?
Просто посылает определённую команду HDD. Например, в ATA она так и называется, FLUSH CACHE, опкод E7h. (здесь должен быть ещё десяток оговорок, которые должны быть интересны только разработчикам драйверов)
FILE_FLAG_NO_BUFFERING при вызове функции CreateFile(). Способ имеет два минуса
FlushFileBuffers()FILE_FLAG_WRITE_THROUGH, но не все устройства этот флаг поддерживаютЕще один способ решения проблемы, это писать не в оригинальный файл, а в его копию. А после завершения записи вызывать функцию ReplaceFile() и заменять исходный файл.
В одной системе у меня используется два идентичных файла данных и к каждому файлу в отдельном файле лежит контрольная сумма. Запись файлов идет в такой последовательности:
По старту системы я считываю основной файл и проверяю его контрольную сумму. Если совпало — работаем. Если нет — эту пару удаляем и проверяем копию.
А вообще все упирается в ценность спасаемых данных. В одной системе мы легко можем потерять базу, восстановить ее из чистого бэкапа и работать. А в другой не спасли ни упсы ни рейды. Экскаватор во дворе зацепил силовой кабель и серверная погибла. Правда в саппорте (вроде тогда еще Dell) сказали ничего не трогать, через неделю приехали специалисты и данные подняли.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей