Реализация файловой системы. Файловая система NTFS
Написание, компиляция и прогон программы захвата части файла для монопольного доступа
Несмотря на то, что у данного механизма захвата файла непростая логика и наследование (см., например [ Харт ] ), рекомендуется самостоятельно разработать небольшую программу, иллюстрирующую данный способ синхронизации. Программа должна продемонстрировать невозможность осуществления операции записи одним процессом в файл, заблокированный другим процессом при помощи функции LockFile.
Производительность файловой системы
Эффективность работы - одна из важнейших задач подсистемы управления файлами.
Кэширование
Самый естественный способ повысить производительность - минимизировать количество обращений к диску за счет кэширования. В общем случае кэширование - использование быстрого устройства для оптимизации работы устройства медленного. В данном случае часть блоков файла, с которыми активно взаимодействует приложение, размещается в буфере оперативной памяти. Периодически производится синхронизация содержимого кэша и диска. Возможность использования кэша в операционных системах обусловлена свойством локальности - объем ключевой информации в каждый момент времени относительно невелик.
Устройство кэша ОС Windows отличается от традиционного. В традиционной реализации кэш - буфер в оперативной памяти, содержащий ряд блоков диска и расположенный между файловой системой и системой ввода-вывода. Если имеется запрос на чтение (запись) в файл, файловая система вычисляет номер блока в файле и номер соответствующего ему блока диска. Перед чтением/записью блока диска производится проверка на предмет наличия этого блока в кэше. Если блок в кэше имеется, то запрос удовлетворяется из кэша, в противном случае запрошенный блок считывается в кэш с диска.
В ОС Windows кэш работает на более высоком уровне, нежели файловая система (см. рис. 12.8).
С помощью техники файлов, отображаемых в память, часть считываемого (записываемого) файла проецируется в 256-килобайтный буфер кэша (см. рис. 12.9).
В результате запрос на чтение с текущей позиции может быть непосредственно удовлетворен из кэша. Если же нужных байтов файла в кэше нет, то файловая система вычисляет логический номер блока в файле (LCN), затем логический номер блока на диске (VCN). После этого делается запрос к системе ввода-вывода на чтение этого блока, точнее, проецирование в буфер кэша части файла, содержащей данный блок. Подобная организация позволяет системе поддерживать единый централизованный кэш для всех используемых файловых систем (NTFS, FAT, CDFS, удаленная FS и др.), а файловые системы не обязаны управлять своими кэшами.
Важным свойством кэша является его когерентность. Менеджер кэша следит за соответствием открытых файлов и файлов, отображаемых в память (с помощью функции MapViewOfFile ). Каждый раз, когда процесс считывает файл или отображает его в память, этот запрос удовлетворяется из кэша путем копирования соответствующего блока в адресное пространство процесса. Поэтому, независимо от того, сколько процессов откроют файл или отобразят его в память, реальное отображение в память происходит один раз. В итоге все процессы будут "видеть" одну и ту же версию файла.
Аккуратная реализация кэширования требует решения нескольких проблем.
Во-первых, емкость буфера кэша ограничена. У системного кэша нет собственного рабочего набора - он входит в единый системный рабочий набор. Размером системного рабочего набора управляет менеджер памяти, отвечающий за его расширение или усечение. При этом величина, помеченная как размер системного кэша на панели диспетчера задач, относится к размеру всего системного рабочего набора.
Во-вторых, поскольку кэширование использует механизм отложенной записи (lazy write), при котором модификация буфера не вызывает немедленной записи на диск, серьезной проблемой является "старение" информации в дисковых блоках кэшируемого файла. Несвоевременная синхронизация буфера кэша и диска может привести к очень нежелательным последствиям в случае отказов оборудования или программного обеспечения.
Записываемые данные в течение некоторого времени накапливаются, после чего сбрасываются на диск пакетом. С точки зрения быстродействия и снижения риска от возможных потерь очень важен выбор частоты сброса кэша. Обычно подсистема отложенной записи раз в секунду сбрасывает на диск одну восьмую количества модифицированных страниц системного кэша.
ОС Windows позволяет организовать вариант синхронного режима работы с отдельными файлами, задаваемый при открытии файла, при котором все изменения в файле немедленно сохраняются на диске. Для этого можно, например, установить флаг FILE_FLAG_WRITE_THROUGH при вызове функции CreateFile. Фактически, это отказ от кэширования и, соответственно, резкое снижение производительности.
Кроме того в любой момент можно принудительно сбросить содержимое кэша открытого файла на диск с помощью функции FlushFileBuffers. Напомним, что для отображаемого файла имеется аналогичная функция FlushViewOfFile.
Прогон программы, иллюстрирующей функционирование кэша
#include <windows.h> #include <stdio.h> void main(void) { HANDLE hFile, hHeap; int iRet = 0; void *pMem; long FileSize = 0, FilePos = 0; DWORD iRead = 0, iWrite = 0; char * String; hFile = CreateFile("MYFILE.TXT", GENERIC_READ| GENERIC_WRITE,0, NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL |0, NULL); if (hFile == INVALID_HANDLE_VALUE) printf("Could not open file \n"); FileSize = GetFileSize(hFile, NULL); printf("FileSize = %d\n",FileSize); hHeap = GetProcessHeap(); pMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, FileSize + 2); String = (char *)pMem; ReadFile(hFile, pMem, FileSize, &iRead, NULL); printf("Read %d bytes \n", iRead); for(FilePos = 0; FilePos < FileSize; FilePos++) String[FilePos] = '1'; SetFilePointer(hFile, 0, NULL, FILE_BEGIN); getchar(); WriteFile(hFile, pMem, FileSize, &iWrite, NULL); printf("Write %d bytes \n", iWrite); iRet = FlushFileBuffers(hFile); if(iRet == 0) printf("FlushFileBuffer Error\n"); HeapFree(hHeap, 0, pMem); CloseHandle(hFile); }
Приведенная программа считывает большой файл (рекомендуемый размер - несколько мегабайт) в буфер памяти. Затем она меняет содержимое буфера и записывает его на диск. При этом программа пытается сбросить содержимое кэша с помощью функции FlushFileBuffers.
За результатами работы программы можно наблюдать с помощью счетчика "сбросов данных" кэша. Счетчик ведет себя в соответствии с рисунком, где максимальный пик появляется вслед за нажатием клавиши "Enter".
Рекомендуется модифицировать данную программу и проанализировать поведение системы отложенного сброса кэша при помощи соответствующих счетчиков производительности.
Оптимальное размещение информации на диске
Кэширование - не единственный способ увеличения производительности системы. Другая важная техника - сокращение количества движений считывающей головки диска за счет разумной стратегии размещения информации. Для этого целесообразно периодически осуществлять дефрагментацию диска (сборку мусора). Дефрагментацию можно выполнить с помощью соответствующей вкладки на административной консоли панели управления.
Hадежность файловой системы
Поскольку разрушение файловой системы зачастую более опасно, чем разрушение компьютера, файловые системы должны разрабатываться с учетом подобной возможности. Сохранность информации может быть обеспечена за счет ее избыточности (резервное копирование, зеркалирование, образование RAID массивов). Файловые системы современных ОС содержат специальные средства для поддержки собственной целостности и непротиворечивости.