Проверьте, действительно ли содержимое файла было записано на диск — не помещено в очередь в буфере контроллера диска

#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 (первый синхронизирует все файловые системы, последний ограничивает область действия файловой системой, соответствующей одному файловому дескриптору).

Для максимальной безопасности вы бы хотели:

  1. Выполните целевое fsync после окончательного fflush , но до rename (чтобы новый файл был завершен на диске перед заменой старого), и
  2. Выполните более широкое sync / syncfs после rename , но перед remove вызовами (чтобы обновления метаданных из rename были завершены до удаления исходных файлов)

Пропустить шаг 1 можно, если вы не возражаете против поврежденных выходных данных в тех случаях, когда входные данные все еще существуют.

Конечно, как я уже сказал, это все лучшие усилия; если контроллер диска обманывает ОС, вы ничего не можете сделать, кроме как написать новую прошивку и драйверы для диска, что, вероятно, заходит слишком далеко.