Вопрос

Я пишу программу, что-то вроде базы данных.Пока я читал руководство по fclose(3) Я обнаружил, что это вызывает fflush(3) чтобы смыть FILE* буферы на диск (фактически в буфер операционной системы, но сейчас это не имеет значения, мы всегда можем вызвать fsync(2)).

Поскольку я пишу базу данных, очевидно, что я хочу предотвратить потерю данных.Если на диске нет свободного места и fflush(3) в fclose(3) сбой — мы потеряем наши данные, потому что

используя FILE* после ошибки в fclose() вызовет неопределенное поведение

Итак, я подумал о явном использовании fflush(3) до того , как fclose(3), предупреждает пользователя о нехватке места на диске и отзывает fflush(3) через некоторое время.

Я прочитал C стандарт и подумал, что это хорошая идея.На практике, после неудачного fflush второй вызов всегда возвращал бы 0 (без ошибки), но фактически ничего бы не сделал. fsync мне это не помогло (я думал, что данные могут быть сохранены в оперативной памяти).

Как я могу предотвратить потерю данных в такой ситуации?Может быть, есть какие-то эмпирические правила.

Вот мой тестовый код:

#include <stdio.h>
int main()
{
    FILE *a = fopen("/tmp/1", "wb")
    if ( !a )
        perror("fopen");

    if ( fwrite("test", 1, 4, a) != 4 )
        perror("fwrite");  // always OK, cause data is buffered


    while( fflush(a) )  // ...second call will always return 0!
    {
        perror("fflush");  // if there is no disk space, I will get this perror, but ...
    }


    if ( fclose(a) )  // always ok, because calls only close(2)
        perror("fclose"); 

    return 0;
}
Это было полезно?

Решение

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

Если вы выполняете запись в базу данных, вы должны быть осторожны при каждой записи, а не просто иметь дело с проблемами в конце.В зависимости от того, насколько важны ваши данные, вам может потребоваться пройти через всевозможные повороты, чтобы справиться с проблемами - существуют причины, по которым СУБД сложны, и неудачная запись - одна из них.

Один из способов решения этой проблемы - предварительно выделить пространство для данных.Как отмечали другие, классические файловые системы Unix допускают разреженные файлы (файлы, в которых есть пустые блоки без выделенного для них дискового пространства), поэтому вам фактически приходится записывать некоторые данные на каждую страницу, которую вам нужно выделить.Тогда вам придется беспокоиться о проблемах с "заполнением диска" только при расширении пространства - и вы знаете, когда вы это сделаете, и вы можете аккуратно справиться с этим сбоем.

В системах на базе Unix существует множество системных вызовов, которые могут помочь вам синхронизировать ваши данные на диске, а также опции "открыть" и т.д.К ним относятся 'O_DSYNC' и связанные с ним значения.Однако, если вы расширяете файл, они все равно могут вызывать сбои из-за "нехватки места", даже при самых популярных параметрах синхронизации.И когда вы действительно сталкиваетесь с этим сбоем, вам приходится ждать, пока освободится место (возможно, потому, что вы попросили пользователя сообщить вам, когда оно будет доступно), а затем повторить попытку записи.

Другие советы

Вы могли бы предварительно выделить некоторый разумный объем дискового пространства.Запишите, сбросьте и синхронизируйте некоторые двоичные нули (или что-то еще), а затем вернитесь туда, где вы были.Промойте и повторите при необходимости.И не забудьте при необходимости обрезать.

Немного больно, но это должно сработать.

fflush только сбросит внутренние буферы библиотеки C в ОС, поэтому fflush не гарантирует, что не произойдет потери данных.

Повторный вызов fflush (без промежуточных операций fwrites) не поможет, так как вы уже один раз сбросили данные в ОС.Второй вызов fflush вернет УСПЕХ, поскольку есть ничего чтобы выполнить сброс к операционной системе.Если сбой fflush произошел из-за переполнения жесткого диска, значит, вы уже потеряли некоторые данные.

Чтобы сбросить данные на диск, вы потребность чтобы использовать fsync.

Если жесткий диск заполнен, вам не повезло.Единственный способ предотвратить потерю данных - теперь поддерживать ваш процесс в рабочем состоянии (и данные в памяти:либо в пользовательском пространстве / файловых буферах ядра), пока вы не найдете на диске немного места для fsync.Теперь, если отключится электричество, вы будет потеряйте данные.

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

Вы могли бы просмотреть (3) до конца файла (при условии, что вы знаете длину), прежде чем что-либо делать.Таким образом, вы исключите возможность сбоя из-за нехватки места на диске.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top