C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Исчезают файлы., Ошибка при открытии, закрытии файла.
tsl
Отправлено: 08.01.2004, 15:14


Дежурный стрелочник

Группа: Участник
Сообщений: 65



В программе критически важная информации переодически сбрасывается на диск. При каждой записи файл перезаписывается. Открывается и закрывается. Длина файла не меняется. Файл имеет длину порядка 1-2 килобайт. Однако цикл открытие — закрытие иногда может повторяться чаще чем раз в полсекунды.

Проблемма: При срыве питания компьтера файл изредка исчезает.

Для решения проблемы создаю дополнительно контрольный файл. При восстановлении программа запрашивает этот файл, и если не может обнаружить использует второй.

Такое решение значительно улучшает ситуацию, но в принципе проблему не решает. Значительно реже, но все же иногда исчезают оба файла.

Как я понимаю, причина во взаимодействии Windows — винчестер. Менеджер памяти или, что в этом роде. Короче, Windows, имея представление файлов в оперативной памяти, не создает их образ на диске при аварийном отключении питания.

У меня два вопроса:
1. Правильно ли я понимаю ситуацию?
2. Какие могут быть средства борьбы и как их применять?

(Например: может быть можно, для конкретных файлов, как то запретить отложеное обновление винчестера)

fellow
Отправлено: 08.01.2004, 18:28


Дежурный стрелочник

Группа: Участник
Сообщений: 33



Для принудительного сброса файлового кэша на диск служит функция Win32 API FlushFileBuffers.
В качестве параметра передаётся HANDLE файла. Возвращаемый результат имеет тип BOOL, ненулевой результат — функция завершилась без ошибки.
Подробности, как всегда, в справке по Win32 API или в MSDN.
Георгий
Отправлено: 08.01.2004, 20:09


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



некоторое время назад защищался от такого рода проблем, причём успешно. Использовал флаг FILE_FLAG_WRITE_THROUGH и выглядело это так:
CODE
hFile=CreateFile( this->WorkFileName.c_str(),
                      GENERIC_READ|GENERIC_WRITE,
                      FILE_SHARE_READ,NULL,
                      OPEN_ALWAYS,
                      FILE_FLAG_WRITE_THROUGH|FILE_FLAG_SEQUENTIAL_SCAN,
                      NULL);
надеюсь, что и Вам поможет.
** pasha
Отправлено: 09.01.2004, 09:40


Не зарегистрирован







Купите бесперебойник — ИБП !
Избавите себя от очень многих проблем.

QUOTE
Проблемма: При срыве питания компьтера файл изредка исчезает.


Потому-что если питание сорвется неудачно — в момент записи на
винчестер, вы можете потерять весь винчестер, не только инфор-
мацию, но и весь его физически придется выкинуть, если головка
винчестера "упадет" на "блин" при отключении питания в момент
записи.

tsl
Отправлено: 11.01.2004, 18:50


Дежурный стрелочник

Группа: Участник
Сообщений: 65



Я не специалист, но, по слухам, головки уже несколько лет как не падают. Винчестеры сейчас устроены так, что это не возможно. В данный момент я провожу тесты на устойчивость к выключению питания. Компьютер внутри устройства. Тема опасности выключения питания для винчестера даже не поднималась. По крайней мере четыре дня винчестеры это выдерживают.

Бесперебойник по некоторым причинам поставлен в устройство не может.

Код создания файла, как не страно, не помог
hFile=CreateFile( this->WorkFileName.c_str(),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,NULL,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH|FILE_FLAG_SEQUENTIAL_SCAN,
NULL);

Написал для отладки я следующий код:
//---------------------------------------------------------------------------
void __fastcall TWriteCycleForm::WriteCycleButtonClick(TObject *Sender)
{
Files = new TFiles();
IterationCount = StrToInt(this->IterationCountEdit->Text);
IterationNumber = 0;
/***
for ( int i = 0; i < IterationCount; i++)
{
}
/***/
//Application->ProcessMessages();
//Application->Terminate();
//abort();
//int i = FILE_FLAG_WRITE_THROUGH;
Enough = false;
Timer2->Interval = StrToInt(this->IntervalEdit->Text);
Timer2->Enabled = false;
Timer1->Interval = StrToInt(this->IntervalEdit->Text);
Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TWriteCycleForm::Timer1Timer(TObject *Sender)
{
Timer1->Enabled = false;
if (Enough) return;
Files->OneSave ("time110.txt");
Files->TwoSave ("time120.txt");
Files->ThreeSave("time130.txt");
Files->OneSave ("time111.txt");
Files->TwoSave ("time121.txt");
Files->ThreeSave("time131.txt");
Files->OneSave ("time112.txt");
Files->TwoSave ("time122.txt");
Files->ThreeSave("time132.txt");
Files->OneSave ("time113.txt");
Files->TwoSave ("time123.txt");
Files->ThreeSave("time133.txt");
IterationNumber++;
if (IterationCount> IterationNumber) Timer2->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TWriteCycleForm::EnoughCheckBoxClick(TObject *Sender)
{
Enough = this->EnoughCheckBox->Checked;
}
//---------------------------------------------------------------------------

void __fastcall TWriteCycleForm::Timer2Timer(TObject *Sender)
{
Timer2->Enabled = false;
if (Enough) return;
Files->OneSave ("time210.txt");
Files->TwoSave ("time220.txt");
Files->ThreeSave("time230.txt");
Files->OneSave ("time211.txt");
Files->TwoSave ("time221.txt");
Files->ThreeSave("time231.txt");
Files->OneSave ("time212.txt");
Files->TwoSave ("time222.txt");
Files->ThreeSave("time232.txt");
Files->OneSave ("time213.txt");
Files->TwoSave ("time223.txt");
Files->ThreeSave("time233.txt");
IterationNumber++;
if (IterationCount> IterationNumber) Timer1->Enabled = true;
}

void TFiles::OneSave(AnsiString FileName)
{
time_t t;time(&t);
FILE *F;
F = fopen((FileName).c_str(),"wt");
fprintf(F,"%d\n",5);
fprintf(F,"%d\n",5);
fclose(F);
F = fopen((FileName).c_str(),"a+");
FlushFileBuffers(F);
fclose(F);
}
void TFiles::TwoSave(AnsiString FileName)
{
time_t t;time(&t);
{
FILE *F;
F = fopen((FileName).c_str(),"wt");
fprintf(F,"%d\n",5);
fprintf(F,"%d\n",5);
fclose(F);
}

}
void TFiles::ThreeSave(AnsiString FileName)
{
if (FileExists(FileName))
{
DeleteFile(FileName);
}
void* hFile;
hFile = CreateFile(
FileName.c_str(),
GENERIC_READ|GENERIC_WRITE,
NULL,//FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,
NULL,
CREATE_ALWAYS,
FILE_FLAG_WRITE_THROUGH|FILE_FLAG_DELETE_ON_CLOSE|FILE_ATTRIBUTE_NORMAL ,
NULL);
CloseHandle(hFile);
{
FILE *F;
F = fopen((FileName).c_str(),"wt");
fprintf(F,"%d\n",5);
fprintf(F,"%d\n",5);
fclose(F);
}
}

Файлы исчезают, но я их обнаруживаю в корневом каталоге в папках вида Found.023 ... и с именами File0000, File0001...
Admin
Отправлено: 11.01.2004, 19:19


Владимир

Группа: Администратор
Сообщений: 1190



И не только файлы, но и Windows может легко навернуться,
(и все файлы на винчестере), если в момент записи на винчестер
будет отключено питание.

QUOTE

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


Насчет устойчивости винчестера — очень сомневаюсь,
думаю что ни один производитель винчестеров на это гарантию
не даст, хоть и пишут что это безопасно

"При падении скорости вращения ниже нормы или выключении питания
процессор винчестера отводит их ближе к шпинделю, в так
называемую "парковочную зону", где головки ложатся на поверхность
диска. Запись информации в этой зоне не производится.
Предварительная парковка необходима для сохранности головок и
поверхности дисков, иначе при соприкосновении головки с
поверхностью диска на такой скорости будет выведена из строя
рабочая поверхность диска и сама головка."
http://www.soobcha.ru/faq/index.html?question=2478

И также:
"При слишком быстром выключении питания компьютера с винчестером
может происходить следующая неприятность. Если сигнал выключения
блока питания приходит раньше, чем диск успел завершить все
операции записи из своего внутреннего кэша непосредственно на
магнитную пластину (при кэше в 2 Мбайт время записи может
составлять несколько десятых долей секунды), то возможна порча
информации на поверхности пластины (но не обязательно, что при
этом испортится поверхность пластины).

Вот что по этому поводу пишет небезызвестная Microsoft: проблема не
привязана к какому-либо конкретному оборудованию и может
встречаться при установке Windows 98/ME на компьютеры с диском
ATA/100, причем чаще для процессоров с частотой 933 МГц и выше.
При выключении Windows содержание виртуального кэша
операционной системы записывается на диск, и если это IDE-диск, то
данные сначала пишутся в его кэш, после чего выдается команда на
выключение компьютера. Если при этом данные из кэша не успели
записаться на пластину, то они теряются, и при последующем
включении компьютера система запускает Scandisk (хотя компьютер
был выключен «правильно»). Признайтесь, сталкивались с подобным?
Более того, в результате этого может возникнуть фатальная потеря
данных вплоть до невозможности загрузки ОС и неприятный шум
головок диска. Проблема характерна только для Windows 98 SE и
Windows ME. Специальные патчи (98ata100.exe и MEata100.exe),
доступные для загрузки через центр windowsupdate.microsoft.com,
решают ее путем добавления задержки при подаче сигнала
выключения блока питания. Аналогичный эффект можно получить,
установив вручную желаемое время задержки в реестре Windows
путем несложной процедуры, описанной на
support.microsoft.com/support/kb/articles/Q273/0/17.asp. Похожая
проблема есть и для SCSI-накопителей (см. ссылку там же).

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

http://cnt.online.ru/mpl/face?id=4666&temp...late_file=print
или
http://www.computerra.ru/offline/2001/394/9207/

Так что попробуйте использовать:
http://www.microsoft.com/windows98/downloa...017/default.asp

А решение всех проблем — бесперебойник.

Отредактировано Admin — 11/01/2004, 20:33
Георгий
Отправлено: 11.01.2004, 21:53


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



хорошая задачка — сделать отказоустойчивую систему используя офисную ОС.
1.5 года назад у меня была чуть чуть другая задача — хранить показания датчиков за последние 3е суток.
сначала сделал по простому — писал в файл и в ОП держал синхронную копию данных — для выборок и т.п.
при тестировании обнаружил такую сложность — при выключении питания файл мог оказаться разрушенным. решил с помошью вышеупомянутого флажка FILE_FLAG_WRITE_THROUGH. после его установки скорость записи упала, но никаких проблем с разрушением файлов не было.
каждый час делал проверку — есть ли в файле замеры старее 4-х дней и если есть, то включал создание архива:
CODE
bool TMemoryDatabase::MakeBackup(const BaseData* from,const AnsiString& ArchName,const AnsiString& tempDir)
{
/*
идея:
скопировать все записи, которые больше или равны заданной во временный файл
отсоединиться от текущего файла
переименавать текущий файл в архивную копию
переименовать временный файл в текущий
подсоединиться к текущему файлу
в результате будет получены:
в текущем файле будут только данные для оперативного отображения
которые уже будут в упорядоченном виде
в файле архива будет находиться всеь обьём информации на момент создания
время захвата данных:
копирование во временный файл = около 30 сек, но нет захвата
отсоединение от текущего файла = ничтожно мало — захват установлен
переименование текущего в архивный = аналогично, но в пределах одного логического диска, для разных около 60 сек
переименование временный файл в текущий = аналогично, для разных около 30 сек
подсоединение к текущему файлу = ничтожно мало — захват снят
итого: для одного логического диска < 1 сек, для разных 90 сек
вывод работа системы гарантирована только если файлы находятся на одном логическом диске
*/
char Buffer[MAX_PATH];
HANDLE hTmp;
int Start,i;
void* buf;
DWORD Size,nBytes;
bool ret=true;
AnsiString FN;
// bool testl;
char*TempFileExt="ISKKZ";
GetTempFileName(tempDir.c_str(),TempFileExt,0,Buffer);
hTmp=CreateFile( Buffer,
GENERIC_WRITE,
NULL,NULL,
OPEN_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (hTmp==INVALID_HANDLE_VALUE)
return false;
Start=this->GetRightIndex(from);//с какого места копировать
if (Start<0)Start=0;
Size=this->Read(Start)->GetSize();
buf=new char[Size];
//начинаем копировать записи во временный файл
for (i=Start;i<this->GetRecCount();i++)
{
try{
this->Read(i)->Read(buf);
}catch(...){continue;};
WriteFile(hTmp,buf,Size,&nBytes,NULL);
if (nBytes!=Size)
{
ret=false;
goto MakeBackupLab1;
};
};
FlushFileBuffers(hTmp);
//копирование завершено
this->t->BeginWrite();
CloseHandle(this->hFile);//отсоединение от файла
CloseHandle(hTmp);
for (i=WorkFileName.Length();i>0;i--)
if (WorkFileName[i]=='\\')
break;
FN=ArchName+WorkFileName.SubString(i,WorkFileName.Length());
/*testl=*/DeleteFile(ArchName.c_str());
/*testl=*/MoveFile(this->WorkFileName.c_str(),FN.c_str());//переименование
/*testl=*/MoveFile(Buffer,this->WorkFileName.c_str());
hFile=CreateFile( this->WorkFileName.c_str(),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ,NULL,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH|FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
/*testl=*/SetFilePointer(hFile,0,NULL,FILE_END);//перенос указателя на конец для добавления
this->t->EndWrite();//работа по подсоединению нового файла завершена
this->DeleteMulti(0,Start-1);
MakeBackupLab1:
delete buf;
return ret;
};

tsl
как видите идея ничем от вашей не отличается, более того ваш алгоритм более надёжен т.к. я делал предположение, что сбоя во время создания архива не будет. оправдывал это тем, что вероятность сбоя с потерей данных около 0,00039% т.е. вероятность работы без ошибок = 99,99961% что значительно превосходило требуемую надёжность.
программа уже 1 год работает и о никаких проблемах с ней мне не известно.
компьютере (обычный PC) работает ОС MS Windows 98SE, раздел, где программа хранит свои файлы, FAT32.

при тестировании на NTFS (MS Windows2000SP1) обнаружил очень интересный эффект — в некоторых случаях, после нажатия reset`а к файла терялся конец — он становился короче и данные за последние 3-4 минуты отсутствовали. на FAT32 такого не видел, поэтому на ней и остановился.
Но файлы ни разу не пропадали. хотя их было около 10-13 из которых 2 обновлялись каждые 3 секунды, а остальные не реже 1 раза в час. Причём у меня даже нет идей, как это они могут пропадать... (если не принимать во внимание те факты о которых Владимир говорит)

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

Отредактировано Георгий — 12/01/2004, 22:45
tsl
Отправлено: 26.01.2004, 18:51


Дежурный стрелочник

Группа: Участник
Сообщений: 65



Георгий у меня просил конец истории с пропажей файлов.

История закончена. Я остановился в своих поисках решения этой проблемы. Проблема проистекала из невозможности поставить устройство бесперебойного питания в разрабатываемое изделие (геометрически ставить некуда) и большой скорости обновления файлов (файл перезаписывался иногда более трех раз в секунду).

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

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

В тестах, проведенных в условиях моей задачи (1-3 сохранения в секунду), откат не превышал 7-ми файлов.

Я на этом остановился. Число хранимых файлов задано, для надежности, в количестве 10 штук.

Вернуться в Вопросы программирования в C++Builder