Страницы

Поиск по вопросам

суббота, 7 декабря 2019 г.

Повреждение файлов при отключении питания и исключениях в процессах

#c #windows #файлы #исключения #embedded


Я занимаюсь разработкой автономных систем управления и анализа для тяжелой промышленности.
Использую язык C (C99-C11). Очень беспокоит вопрос повреждения файлов и файловых систем,
использующихся в Windows (Embedded, CE и пр.) при внезапном обесточивании или при жестком
прерывании рабочего процесса/потока, например из-за исключения.

Хотелось бы разобраться в этом вопросе, достойной литературы по этой теме найти не
удалось. Посоветуйте что-нибудь, желательно не на английском языке. Или, может быть,
кто-нибудь сможет упрощенно объяснить следующее:


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

Но ведь даже если мы не работаем с файлом, а в этот момент отключается питание, файл
может быть поврежден из-за того, что ОС в фоновом режиме занимается дефрагментацией/индексацией.
Это так?
Есть ли гарантия, что после возврата управления в программу после функций fclose()
или fwrite() данные гарантированно корректно занесены на носитель?

Например, ситуация: 


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


Я предполагал защитить файл критической секцией, и использовать эту же критическую
секцию для инициации аварийного завершения. Тогда, предположительно, после того как
работа с файлом закончится, критическая секция освободится, и второй поток сможет в
нее зайти и вызвать abort() или аналогичную функцию планируемого жесткого прерывания
процесса.

Проблема на мой взгляд в том, что даже после того, как мы сделали fopen() + fwrite()
+ fclose(), нет гарантий, что файл корректно сохранен, а не гуляет где-то в буферах,
которые связаны с нашим процессом.
И как контролируется процесс переноса данных из внутреннего промежуточного буфера-кэша
жесткого диска на сам носитель?

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

Как ОС отслеживает перед своим завершением факт того, что данные из буфера накопителя
записаны окончательно?

    


Ответы

Ответ 1



Но ведь даже если мы не работаем с файлом, а в этот момент отключается питание, файл может быть поврежден из-за того, что ОС в фоновом режиме занимается дефрагментацией/индексацией. Это так? Во-первых, сомневаюсь, что драйвер самостоятельно будет дефрагментировать что-либо (хотя и не буду утверждать обратное), а отсутствие насущности проблемы фрагментации объясняется в первую очередь грамотными алгоритмами выбора блоков для хранения файлов. А критические, неустранимые ошибки ФС в таких случаях — это практически невероятный сценарий (скажем, внезапный отказ HDD — куда более вероятен). Но повреждения данных (файлов) с которыми велась активная работа в момент отказа — штатная ситуация. 2) Есть ли гарантия, что после возврата управления в программу после функций fclose() или fwrite() данные гарантированно корректно занесены на носитель? Нет. fwrite () гарантирует только занесение данных в пользовательский буфер, но не гарантирует, что эти данные будут переданы ОС. Чтобы принудительно сбросить пользовательский буфер есть функция fflush (). При скоропостижной кончине процесса данные могут быть потеряны. fclose () и fflush () сбрасывают пользовательский буфер, но не гарантируют, что ОС передаст данные на носитель. Если процесс умрёт после их возврата, то изменения не потеряются, но они могут быть потеряны в случае внезапного отключения питания. 3) И как контролируется процесс переноса данных из внутреннего промежуточного буфера-кэша жесткого диска на сам носитель? Для того чтобы гарантировать запись данных на диск средств Си недостаточно, необходимо пользоваться API ОС. В POSIX-системах (и в linux в частности) есть вызов fsync (). Он гарантирует, что вернёт управление (само собой без ошибки) только после того как данные физически попадут на диск и переживут внезапный отказ системы. В Win API аналогом FlushFileBuffers (), согласно документации он сбрасывает системные буферы, но, гарантирует ли сброс буферов самого HDD или нет, я не знаю. Как ОС отслеживает перед своим завершением факт того, что данные из буфера накопителя записаны окончательно? Просто посылает определённую команду HDD. Например, в ATA она так и называется, FLUSH CACHE, опкод E7h. (здесь должен быть ещё десяток оговорок, которые должны быть интересны только разработчикам драйверов)

Ответ 2



Гарантированную запись на диск можно достичь использованием флага FILE_FLAG_NO_BUFFERING при вызове функции CreateFile(). Способ имеет два минуса Замедление операций чтения/записи. Чтобы этого избежать, нужно работать с файлом большими буферами, кратными разделу кластера диска. Альтернативой использования этого флага является функция FlushFileBuffers() Этот флаг гарантирует, что ОС не будет использовать дополнительных буферов, но буферизация есть еще на самом жестком диске. Чтобы отключить буферизацию диска нужно использовать флаг FILE_FLAG_WRITE_THROUGH, но не все устройства этот флаг поддерживают Еще один способ решения проблемы, это писать не в оригинальный файл, а в его копию. А после завершения записи вызывать функцию ReplaceFile() и заменять исходный файл. В одной системе у меня используется два идентичных файла данных и к каждому файлу в отдельном файле лежит контрольная сумма. Запись файлов идет в такой последовательности: Запись основного файла Запись контрольной суммы этого файла Запись копии Запись контрольной суммы копии По старту системы я считываю основной файл и проверяю его контрольную сумму. Если совпало — работаем. Если нет — эту пару удаляем и проверяем копию. Ну и классическое решение задачи — зеркальный RAID массив А вообще все упирается в ценность спасаемых данных. В одной системе мы легко можем потерять базу, восстановить ее из чистого бэкапа и работать. А в другой не спасли ни упсы ни рейды. Экскаватор во дворе зацепил силовой кабель и серверная погибла. Правда в саппорте (вроде тогда еще Dell) сказали ничего не трогать, через неделю приехали специалисты и данные подняли.

Комментариев нет:

Отправить комментарий