fflush и 'не осталось места на диске' - PullRequest
0 голосов
/ 07 февраля 2010

Я пишу программу, какую-то базу данных. Когда я читал руководство по 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;
}

Ответы [ 4 ]

3 голосов
/ 07 февраля 2010

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

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

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

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

1 голос
/ 07 февраля 2010

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

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

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

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

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

1 голос
/ 07 февраля 2010

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

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

0 голосов
/ 07 февраля 2010

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

...