Для описания ссылок на физическую память служит список дескрипторов памяти (memory descriptor list, MDL) (см. главу 7). DMA-интерфейс диспетчера кэша состоит их четырех функций (таблица 11-8).
Вы можете исследовать активность, связанную с MDL-чтением из кэша, через счетчики производительности или системные переменные, перечисленные в таблице 11-9.
Быстрый ввод-вывод
Операции чтения и записи, выполняемые над кэшируемыми файлами, по возможности обрабатываются с применением высокоскоростного механизма — быстрого eeoдa-вывода (fast I/O). Как уже говорилось в главе 9, быстрый ввод-вывод обеспечивает чтение и запись кэшируемых файлов без генерации IRR При использовании этого механизма диспетчер ввода-вывода вызывает процедуру быстрого ввода-вывода, принадлежащую драйверу файловой системы, и определяет, можно ли удовлетворить ввод-вывод непосредственно из кэша без генерации IRR
Поскольку диспетчер кэша в архитектуре системы размещается поверх подсистемы виртуальной памяти, драйверы файловых систем могут использовать этот диспетчер для доступа к данным путем простого копирования их в страницы (или из страниц), проецируемые на тот файл, на который ссылается пользовательская программа, без генерации IRR.
Быстрый ввод-вывод возможен не всегда. Например, первая операция чтения или записи требует подготовки файла к кэшированию (его проецирования на кэш и создания структур данных кэша, описанных в разделе «Структуры данных кэша» ранее в этой главе). Быстрый ввод-вывод не применяется и в том случае, если вызывающий поток указывает асинхронное чтение или запись, поскольку этот поток может быть приостановлен в ходе операций ввода-вывода, связанных с подкачкой и необходимых для копирования буферов в системный кэш (и из него), и фактически синхронного выполнения запрошенной операции асинхронного ввода-вывода. Однако даже при синхронном вводе-выводе драйвер файловой системы может решить, что обработка запрошенной операции по механизму быстрого ввода-вывода недопустима, если, например, в нужном файле заблокирован какой-то диапазон байтов (в результате вызова Windows-функции LockFile). Поскольку диспетчер кэша не знает, какие части и каких файлов блокированы, драйвер файловой системы должен проверить возможность чтения или записи запрошенных данных, а это требует генерации IRR Алгоритм принятия решений показан на рис. 11–14.
Обслуживание чтения или записи с использованием быстрого ввода-вывода включает следующие операции.
1. Поток выполняет операцию чтения или записи.
2. Если файл кэшируется и указан синхронный ввод-вывод, запрос передается входной точке быстрого ввода-вывода драйвера файловой системы. Если файл не кэшируется, драйвер файловой системы готовит файл к кэшированию, чтобы выполнить следующий запрос на чтение или запись за счет быстрого ввода-вывода.
3. Если процедура драйвера файловой системы, отвечающая за быстрый ввод-вывод, определяет, что быстрый ввод-вывод возможен, она вызывает процедуру чтения или записи диспетчера кэша для прямого доступа к данным кэша. (Если быстрый ввод-вывод невозможен, драйвер файловой системы возвращает управление подсистеме ввода-вывода, которая затем генерирует IRP и в конечном счете вызывает в файловой системе обычную процедуру чтения.)
4. Диспетчер кэша транслирует переданное смещение в файле в виртуальный адрес данных в кэше.
5. При операциях чтения диспетчер кэша копирует данные из кэша в буфер процесса, а при операциях записи — из буфера процесса в кэш.
6. Выполняется одна из следующих операций:
• при операциях чтения из файла, при открытии которого не был установлен флаг FILE_FLAG_RANDOM_ACCESS, в закрытой карте кэша вызывающего потока обновляется информация, необходимая для опережающего чтения;
• при операциях записи устанавливается бит изменения у всех модифицированных страниц кэша, чтобы подсистема отложенной записи сбросила эти страницы на диск;
• для файлов, требующих сквозной записи, все измененные данные немедленно сбрасываются на диск.
ПРИМЕЧАНИЕ Быстрый ввод-вывод возможен не только в тех случаях, когда запрошенные данные уже находятся в физической памяти. Как видно из пп. 5 и 6 предыдущего списка, диспетчер кэша просто обращается по виртуальным адресам уже открытого файла, где он предполагает найти нужные данные. Если происходит промах кэша, диспетчер памяти динамически подгружает эти данные в физическую память.
Счетчики производительности и системные переменные, перечисленные в таблице 11–10, позволяют наблюдать за операциями быстрого ввода-вывода в системе.
Опережающее чтение и отложенная запись
Здесь вы увидите, как диспетчер кэша реализует чтение и запись файловых данных в интересах драйверов файловых систем. Учтите, что диспетчер кэша участвует в файловом вводе-выводе только при открытии файла без флага FILE_FLAG_NO_BUFFERING и последующем чтении или записи через Windows-функции ввода-вывода (например, функции ReadFile и WriteFile). Кроме того, диспетчер кэша не имеет дела с проецируемыми файлами, а также с файлами, открытыми с флагом FILE_FLAG_NO_BUFFERING.
Интеллектуальное опережающее чтение
Для реализации интеллектуального опережающего чтения (intelligent read-ahead) диспетчер кэша использует принцип пространственной локальности (spatial locality); исходя из данных, которые вызывающий процесс считывает в данный момент, диспетчер кэша пытается предсказать, какие данные тот будет считывать в следующий раз. Поскольку системный кэш опирается на использование виртуальных адресов, непрерывных для конкретного файла, их непрерывность в физической памяти не имеет значения. Реализация опережающего чтения файлов при кэшировании на основе логических блоков была бы гораздо сложнее и потребовала бы тесной координации между драйверами файловых систем и кэшем, поскольку такая система кэширования опирается на относительные позиции затребованных данных на диске, а файлы вовсе не обязательно хранятся в непрерывных областях диска. Активность, связанную с опережающим чтением, можно исследовать с помощью счетчика производительности Cache: Read Aheads/Sec (Кэш: Упреждающих чтений/сек) или системной переменной CcReadAheadIos.
Считывание следующего блока файла, к которому происходит последовательное обращение, дает очевидные преимущества. Чтобы распространить эти преимущества и на случаи произвольного (прямого) доступа к данным (в направлении вперед или назад), диспетчер кэша запоминает последние два запроса на чтение в закрытой карте кэша, сопоставленной с описателем файла, к которому обращается программа. Этот метод называется асинхронным опережающим чтением с хронологией. Диспетчер кэша пытается выявить какую-то закономерность в операциях прямого чтения вызывающей программы. Например, если вызывающая программа считывает сначала страницу 4000, затем 3000, диспетчер кэша предполагает, что в следующий раз будет затребована страница 2000, и заблаговременно считывает ее в кэш.
ПРИМЕЧАНИЕ Хотя предсказание возможно лишь на основе последовательности из трех операций чтения минимум, в закрытой карте кэша запоминаются только две из них.
Чтобы еще больше повысить эффективность опережающего чтения, Windows-функция CreateFile поддерживает флаг последовательного доступа к файлу, FILE_FLAG_SEQUENTIAL_SCAN. Если этот флаг задан, диспетчер кэша не ведет хронологию чтения для предсказаний, выполняя вместо этого последовательное опережающее чтение. Ho по мере считывания файла в рабочий набор кэша диспетчер кэша удаляет проекции неактивных представлений файла и командует диспетчеру памяти переместить страницы, принадлежавшие удаленным проекциям, в начало списка простаивающих или модифицированных страниц (если страницы изменены), чтобы впоследствии их можно было быстро использовать повторно. Он также заранее считывает двукратный объем данных (например, 128 Кб вместо 64 Кб). По мере того как вызывающий поток продолжает считывать данные, диспетчер кэша считывает дополнительные блоки данных, всегда опережая вызывающий поток на один блок, равный текущему запрошенному.
B этом случае опережающее чтение выполняется диспетчером кэша асинхронно, так как это делается в контексте отдельного потока, выполняемого параллельно с вызывающим потоком. Когда диспетчер кэша вызывается для выдачи кэшированных данных, он сначала обращается к запрошенной виртуальной странице, чтобы удовлетворить запрос, а затем ставит в очередь системного рабочего потока еще один запрос на ввод-вывод для выборки дополнительной порции данных. Далее рабочий поток выполняется в фоновом режиме и считывает дополнительные данные, упреждая следующий запрос вызывающего потока. Заранее считанные страницы загружаются в память параллельно выполнению пользовательской программы, так что на момент выдачи ее потоком очередного запроса эти данные уже находятся в памяти.