#c #file-handling #stdio
#c #обработка файлов #stdio
Вопрос:
Я написал программу, которая сжимает два небольших файла в один файл большего размера. Сначала я считываю данные из входных файлов, объединяю данные и записываю выходные данные во временный файл. Как только это завершится, я переименую временный файл в нужное имя файла (расположенный в том же разделе на диске). Вот псевдокод:
FILE* fp_1 = fopen("file_1.dat", "r b");
FILE* fp_2 = fopen("file_2.dat", "r b");
FILE* fp_out = fopen("file_tmp.dat", "w b");
// 1. Read data for the key in two files
const char* data_1 = ...;
const char* data_2 = ...;
// 2. Merge data, store in an allocated buffer
// 3. Write merged buffer to temp file
fwrite(temp_buff, estimated_size, 1, fp_out);
fflush(fp_out);
fclose(fp_1);
fclose(fp_2);
fclose(fp_out);
// Now rename temp file to desired file name
if(std::rename("file_tmp.dat", "file_out.dat") == 0)
{
std::remove("file_1.dat");
std::remove("file_2.dat");
}
Я неоднократно тестировал программу с двумя входными файлами по 5 МБ каждый. Однажды я внезапно выключил систему, отсоединив кабель питания. После перезапуска системы я проверил данные и обнаружил, что входные файлы были удалены, а файл file_out.dat
был заполнен всеми нулями. Это заставило меня поверить, что система вышла из строя сразу после удаления 2 входных файлов, а выходные данные все еще находились где-то в буфере контроллера диска. Если это так, то есть ли какой-либо способ проверить, действительно ли данные были записаны на диск?
Комментарии:
1. Поскольку ОС даже не знает наверняка (диск, вероятно, имеет собственный кэш оперативной памяти), это сложная проблема.
Ответ №1:
Не в общем случае. Даже если вы скажете ОС подождать, пока данные не будут записаны (с sync
помощью семейства API), некоторые диски лгут ОС, заявляя, что запись завершена, хотя на самом деле она просто стоит в очереди во встроенном кэше оперативной памяти жесткого диска, который будет потерян при резком отключении питания.
Лучшее, что вы можете сделать, это явно попросить ОС сообщить диску «действительно, действительно синхронизировать все и блокировать, пока это не будет сделано» после того, как вы выполнили fflush
(который только сообщает библиотеке stdio отправлять все данные в буфере пользовательского режима в ОС, которая часто хранит их в буферах ядра исинхронизирует буферы ядра с диском позже, в фоновом режиме), либо с ограниченной областью fsync
действия, либо с использованием чего-то вроде sync
или syncfs
(первый синхронизирует все файловые системы, последний ограничивает область действия файловой системой, соответствующей одному файловому дескриптору).
Для максимальной безопасности вы бы хотели:
- Выполните целевое
fsync
после окончательногоfflush
, но доrename
(чтобы новый файл был завершен на диске перед заменой старого), и - Выполните более широкое
sync
/syncfs
послеrename
, но передremove
вызовами (чтобы обновления метаданных изrename
были завершены до удаления исходных файлов)
Пропустить шаг 1 можно, если вы не возражаете против поврежденных выходных данных в тех случаях, когда входные данные все еще существуют.
Конечно, как я уже сказал, это все лучшие усилия; если контроллер диска обманывает ОС, вы ничего не можете сделать, кроме как написать новую прошивку и драйверы для диска, что, вероятно, заходит слишком далеко.