Второе исключение составляет файл, в котором хранится трассировочная информация, полученная в процессе загрузки системы, – ему всегда присваивается имя NTOSBOOT-B00DFAAD.PF. Средство предвыборки собирает информацию об ошибках страниц для конкретных приложений только после того, как закончит мониторинг процесса загрузки системы.
 
    ЭКСПЕРИМЕНТ: просмотр содержимого файла предвыборки
   Содержимое этого файла содержит записи о файлах и каталогах, к которым было обращение при загрузке системы или запуске приложения, и для их просмотра можно использовать утилиту Strings с сайта wwwsysinternals.com .Следующая команда перечисляет все файлы и каталоги, на которые были ссылки при последней загрузке системы:
 
    C:\Windows\Prefetch›Strings ntosboot-boodfaad.pf Strings v2.1
    Copyright (C) 1999-2003 Mark Russinovich Systems Internals – www.sysinternals.com
 
    NT0SB00T SCCA
    \DEVICE\HARDDISKV0LUME2\$MFT
    \DEVICE\HARDDISKV0LUME2\WIND0WS\PREFETCH\NT0SB00T-B00DFAAD.PF
    \DEVICE\HARDDISKV0LUME2\SYSTEM VOLUME INF0RMATI0N\_REST0RE{
 
    987E0331-0F01-427C-A58A-7A2E4AABF84D}\RP24\CHANGE.L0G
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\PR0CESSR.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\FGLRYM.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\VIDE0PRT.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\E1000325.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\USBUHCI.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\USBP0RT.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\USBEHCI.SYS
    \DEVICE\HARDDISKV0LUME2\WIND0WS\SYSTEM32\DRIVERS\NIC1394.SYS
 
   Средство предвыборки вызывается при загрузке системы или запуске приложения, чтобы оно могло выполнить предварительную выборку. Средство предвыборки просматривает каталог Prefetch и проверяет, есть ли в нем какой-нибудь файл с трассировочной информацией, необходимый для текущего варианта предварительной выборки. Если такой файл имеется, оно обращается к NTFS для предварительной выборки любых ссылок файла метаданных MFT, считывает содержимое каждого каталога, на который есть ссылка, а затем открывает все файлы в соответствии со списком ссылок. Далее вызывается функция MmPrefetchPagesдиспетчера памяти, чтобы загрузить в память любые данные и код, указанные в трассировочной информации, но пока отсутствующие в памяти. Диспетчер памяти инициирует все необходимые операции как асинхронные и ждет их завершения, прежде чем разрешить продолжение процесса запуска приложения.
 
    ЭКСПЕРИМЕНТ: наблюдение за чтением и записью файла предвыборки
   Если вы запишете трассировку запуска приложения с помощью FiIemon ( wwwsysinternals.com )в Windows XP, то заметите, что средство предвыборки проверяет наличие файла предвыборки и, если он есть, считывает его содержимое, а примерно через десять секунд от начала запуска приложения средство предвыборки записывает новую копию этого файла. Ниже показан пример для процесса запуска Notepad (фильтр Include был установлен как «prefetch», чтобы Filemon сообщал об обращениях только к каталогу \Windows\Prefetch).
   Строки 1-3 показывают, что файл предвыборки Notepad считывался в контексте процесса Notepad в ходе его запуска. Строки 4-10 (с временными метками на 10 секунд позже, чем в первых трех строках) демонстрируют, что Task Scheduler, который выполняется в контексте процесса Svchost, записал обновленный файл предвыборки.
   Чтобы еще больше уменьшить вероятность скачкообразного поиска, через каждые три дня (или около того) Task Scheduler в периоды простоя формирует список файлов и каталогов в том порядке, в каком на них были ссылки при загрузке системы или запуске приложения, и сохраняет его в файле \Windows\Prefetch\Layout.ini(рис. 7-32).
   Далее он запускает системный дефрагментатор, указывая ему через командную строку выполнить дефрагментацию на основе содержимого файла Layout.ini. Дефрагментатор находит на каждом томе непрерывную область, достаточно большую, чтобы в ней уместились все файлы и каталоги, перечисленные для данного тома, а затем целиком перемещает их в эту область в указанном порядке. Благодаря этому будущие операции предварительной выборки окажутся еще эффективнее, поскольку все считываемые данные теперь физически хранятся на диске в нужной последовательности. Такая дефрагментация обычно затрагивает всего несколько сотен файлов и поэтому выполняется гораздо быстрее, чем полная дефрагментация диска. (Подробнее о дефрагментации см. в главе 12.)
 
Правила размещения
   Когда поток вызывает ошибку страницы, диспетчер памяти должен также определить, в каком участке физической памяти следует разместить виртуальную страницу. При этом он руководствуется правилами размещения(placement policy). Выбирая фреймы страниц, Windows учитывает размер кэшей процессора и стремится свести нагрузку на них к минимуму.
   Если на момент появления ошибки страницы физическая память заполнена, выбирается страница, подлежащая выгрузке на диск для освобождения памяти под новую страницу. Этот выбор осуществляется по правилам замены(replacement policy). При этом действуют два общих правила замены: LRU (least recently used) и FIFO (first in, first out). Алгоритм LRU (также известный как алгоритм часов и реализованный в большинстве версий UNIX) требует от подсистемы виртуальной памяти следить за тем, когда используется страница в памяти. Страница, не использовавшаяся в течение самого длительного времени, удаляется из рабочего набора. Алгоритм FIFO работает проще: он выгружает из физической памяти страницу, которая находилась там дольше всего независимо от частоты ее использования.
   Правила замены страниц могут быть глобальными или локальными. Глобальные правила позволяют использовать для обработки ошибки страницы любой фрейм страниц независимо от того, принадлежит ли он другому процессу. Например, в результате применения глобальных правил замены с применением алгоритма FIFO будет найдена и выгружена на диск страница, находившаяся в памяти наибольшее время, а локальные правила замены ограничат сферу поиска самой старой страницей из набора, который принадлежит процессу, вызвавшему ошибку страницы. Таким образом, глобальные правила замены делают процессы уязвимыми от поведения других процессов, и одно сбойное приложение может негативно отразиться на всей операционной системе.
   B Windows реализована комбинация локальных и глобальных правил замены. Когда размер рабочего набора достигает своего лимита и/или появляется необходимость его усечения из-за нехватки физической памяти, диспетчер памяти удаляет из рабочих наборов ровно столько страниц, сколько ему нужно освободить.
 
Управление рабочими наборами
   Все процессы начинают свой жизненный цикл с максимальным и минимальным размерами рабочего набора по умолчанию – 50 и 345 страниц соответственно. Хотя это мало что дает, эти значения по умолчанию можно изменить для конкретного процесса через Windows-функцию SetProcessWorkingSetSize,но для этого нужна привилегия Increase Scheduling Priority. Однако, если только вы не укажете процессу использовать жесткие лимиты на рабочий набор (новшество Windows Server 2003), эти лимиты игнорируются в том смысле, что диспетчер памяти разрешит процессу расширение за установленный максимум при наличии интенсивной подкачки страниц и достаточного объема свободной памяти (либо, напротив, уменьшит его рабочий набор ниже минимума при отсутствии подкачки страниц и при высокой потребности системы в физической памяти). Хотя в Windows 2000 степень расширения процесса за максимальную границу рабочего набора увязывалась с вероятностью его усечения, в Windows XP это решение принимается исключительно на основе того, к скольким страницам обращался процесс.
   B Windows Server 2003 жесткие лимиты на размеры рабочего набора могут быть заданы вызовом функции SetProcessWorkingSetSizeExс флагом QUOTA_LIMITS_HARDWS_ENABLE. Этой функцией пользуется, например, Windows System Resource Manager fWSRM), описанный в главе 6.
   Максимальный размер рабочего набора не может превышать общесистемный максимум, вычисленный при инициализации системы и хранящийся в переменной ядра MmMaximumWorkingSetSize.Это значение представляет собой число страниц, доступных на момент вычислений (суммарный размер списков обнуленных, свободных и простаивающих страниц), за вычетом 512 страниц. Однако существуют жесткие верхние лимиты на размеры рабочих наборов – они перечислены в таблице 7-17.
   Когда возникает ошибка страницы, система проверяет лимиты рабочего набора процесса и объем свободной памяти. Если условия позволяют, диспетчер памяти разрешает процессу увеличить размер своего рабочего набора до максимума (и даже превысить его, если свободных страниц достаточно и если для этого процесса не задан жесткий лимит на размер рабочего набора). Ho если памяти мало, Windows предпочитает заменять страницы в рабочем наборе, а не добавлять в него новые.
   Хотя Windows пытается поддерживать достаточный объем доступной памяти, записывая измененные страницы на диск, при слишком быстрой генерации модифицированных страниц понадобится больше свободной памяти. Поэтому, когда свободной физической памяти становится мало, вызывается диспетчер рабочих наборов(working set manager), который выполняется в контексте системного потока диспетчера настройки баланса (см. следующий раздел) и инициирует автоматическое усечение рабочего набора для увеличения объема доступной в системе свободной памяти. (Используя Windows-функцию SetProcessWorkingSetSize,вы можете вызвать усечение рабочего набора своего процесса, например после его инициализации.)
   Диспетчер рабочих наборов принимает решения об усечении каких-либо рабочих наборов, исходя из объема доступной памяти. Если памяти достаточно, он подсчитывает, сколько страниц можно при необходимости изъять из рабочего набора. Как только такая необходимость появится, он уменьшит рабочие наборы, размер которых превышает минимальный. Диспетчер рабочих наборов динамически регулирует частоту проверки рабочих наборов и оптимальным образом упорядочивает список процессов – кандидатов на усечение рабочего набора. Например, первыми проверяются процессы со множеством страниц, к которым не было недавних обращений; часто простаивающие процессы большего размера являются более вероятными кандидатами, чем реже простаивающие процессы меньшего размера; процессы активного приложения рассматриваются в последнюю очередь и т. д.
   Определив, что размеры рабочих наборов процессов превышают минимальные значения, диспетчер ищет страницы, которые можно удалить из их рабочих наборов и сделать доступными для использования в других целях. Если свободной памяти по-прежнему не хватает, диспетчер продолжает удалять страницы из рабочих наборов процессов, пока в системе не появится минимальное количество свободных страниц.
   B однопроцессорных системах Windows 2000 и всех системах Windows XP или Windows Server 2003 диспетчер рабочих наборов старается удалять страницы, к которым не было обращений в последнее время. Такие страницы обнаруживаются проверкой битового флага Accessed в аппаратном РТЕ. Если этот флаг сброшен, страница считается устаревшей, и соответствующий счетчик увеличивается на 1, показывая, что с момента последнего сканирования данного рабочего набора к этой странице не было обращений. Впоследствии возраст страниц учитывается при поиске кандидатов на удаление из рабочего набора.
 
    ПРИМЕЧАНИЕ B многопроцессорной системе Windows 2000 диспетчер рабочего набора ошибочно не проверял битовый флаг Accessed, что приводило к удалению страниц из рабочего набора без учета состояния этого флага.
 
   Если битовый флаг Accessed в аппаратном PTE установлен, диспетчер рабочих наборов сбрасывает его и проверяет следующую страницу рабочего набора. Таким образом, если при следующем сканировании битовый флаг Accessed окажется сброшенным, диспетчер будет знать, что со времени последнего сканирования к этой странице не было обращений. Сканирование списка рабочего набора продолжается до удаления нужного числа страниц или до возврата к начальной точке. (B следующий раз сканирование начнется там, где оно остановилось в прошлый раз.)
 
    ЭКСПЕРИМЕНТ: просмотр размеров рабочих наборов процессов
   C этой целью можно использовать счетчики в оснастке Performance (Производительность), перечисленные в следующей таблице.
   Несколько других утилит для просмотра сведений о процессах (Task Manager, Pview и Pviewer) тоже показывают размеры рабочих наборов.
   Суммарный размер рабочих наборов всех процессов можно получить, выбрав процесс _Total в оснастке Performance. Этот несуществующий процесс просто представляет суммарные значения счетчиков всех процессов, выполняемых в системе в данный момент. Однако суммарный размер не соответствует истине, так как при подсчете размера рабочего набора процесса учитываются его разделяемые страницы. B итоге страница, разделяемая двумя или более процессами, засчитывается в размер рабочего набора каждого из таких процессов.
 
    ЭКСПЕРИМЕНТ: просмотр списка рабочего набора
   Элементы рабочего набора можно увидеть с помощью команды !wsleотладчика ядра. Ниже показан фрагмент выходной информации о списке рабочего набора, полученной с помощью LiveKd (эта команда была выполнена применительно к процессу LiveKd).
   Заметьте, что одни элементы списка рабочего набора представляют собой страницы, содержащие таблицы страниц (элементы с адресами выше OxCOOOOOOO), другие – страницы системных DLL (в диапазоне 0x7nnnnnnn),третьи – страницы кода самой LiveKd.exe (в диапазоне 0x004nnnnn).
 
Диспетчер настройки баланса и подсистема загрузки-выгрузки
   Расширение и усечение рабочего набора выполняется в контексте системного потока диспетчера настройки баланса (balance set manager) (процедура KeBalanceSetManagef).Его поток создается при инициализации системы. Хотя с технической точки зрения диспетчер настройки баланса входит в состав ядра, для анализа и регулировки рабочих наборов он обращается к диспетчеру рабочих наборов.
   Диспетчер настройки баланса ждет на двух объектах «событие»: один из них освобождается по сигналу таймера, срабатывающего раз в секунду, а другой представляет собой внутреннее событие диспетчера рабочих наборов, освобождаемое диспетчером памяти, когда возникает необходимость в изменении рабочих наборов. Например, если в системе слишком часто генерируются ошибки страниц или список свободных страниц слишком мал, диспетчер памяти пробуждает диспетчер настройки баланса, а тот вызывает диспетчер рабочих наборов для усечения таких наборов. Если свободной памяти много, диспетчер рабочих наборов разрешает процессам, часто вызывающим ошибки страниц, постепенно увеличивать размеры своих рабочих наборов, подкачивая в память страницы, при обращении к которым возникали ошибки. Однако рабочие наборы расширяются лишь по мере необходимости.
 
   Диспетчер настройки баланса, пробуждаемый в результате срабатывания таймера, выполняет следующие операции.
   1. При каждом четвертом пробуждении из-за срабатывания таймера освобождает событие, которое активизирует системный поток, выполняющий процедуру KeSwapProcessOrStack –подсистему загрузки-выгрузки (swapper).
   2. Проверяет ассоциативные списки и регулирует глубину их вложения, если это необходимо (для ускорения доступа, снижения нагрузки на пул и уменьшения его фрагментации).
   3. Ищет потоки, чей приоритет может быть повышен из-за нехватки процессорного времени (см. раздел «Динамическое повышение приоритета при нехватке процессорного времени» главы 6).
   4. Вызывает диспетчер рабочих наборов (имеющий собственные внутренние счетчики, которые определяют, когда и насколько агрессивно следует проводить усечение рабочих наборов).
 
   Подсистема загрузки-выгрузки пробуждается и планировщиком, если стек ядра потока, подлежащего выполнению, или весь процесс выгружен в страничный файл. Подсистема загрузки-выгрузки ищет потоки, которые находились в состоянии ожидания в течение 7 секунд fWindows 2000) или 15 секунд (Windows XP и Windows Server 2003). Если такой поток есть, подсистема загрузки-выгрузки переводит его стек ядра в переходное состояние (перемещая соответствующие страницы в список модифицированных или простаивающих страниц). Здесь действует принцип «если поток столько времени ждал, подождет и еще». Когда из памяти удаляется стек ядра последнего потока процесса, этот процесс помечается как полностью выгруженный. Вот почему у долго простаивавших процессов (например, у Winlogon после вашего входа в систему) может быть нулевой размер рабочих наборов.
 
Системный рабочий набор
   Подкачиваемый код и данные операционной системы тоже управляются как единый системный рабочий набор(system working set). B системный рабочий набор могут входить страницы пяти видов:
    (o)системного кэша;
    (o)пула подкачиваемой памяти;
    (o)подкачиваемого кода и данных Ntoskrnl.exe;
    (o)подкачиваемого кода и данных драйверов устройств;
    (o)проецируемых системой представлений.
 
   Выяснить размер системного рабочего набора и пяти его элементов можно с помощью счетчиков производительности или системных переменных, перечисленных в таблице 7-18. Учтите, что значения счетчиков выражаются в байтах, а значения системных переменных – в страницах.
   Узнать интенсивность подкачки страниц в системном рабочем наборе позволяет счетчик Memory: Cache Faults/Sec (Память: Ошибок кэш-памяти/ сек), который сообщает число ошибок страниц, генерируемых в системном рабочем наборе (как аппаратных, так и программных).
   Внутреннее название этого рабочего набора – рабочий набор системного кэша, хотя системный кэш является лишь одним из пяти элементов системного рабочего набора. Из-за этой путаницы некоторые утилиты, сообщая размер файлового кэша, на самом деле показывают суммарный размер системного рабочего набора.
   Минимальный и максимальный размеры системного рабочего набора вычисляются при инициализации системы, исходя из объема физической памяти компьютера и выпуска Windows – клиентского или серверного.
   Вычисленные значения максимального и минимального размеров хранятся в системных переменных, показанных в таблице 7-19(их значения недоступны через счетчики производительности, но вы можете просмотреть их в отладчике ядра).
   Вы можете отдать приоритет системному рабочему набору (в противоположность рабочим наборам процессов), изменив параметр реестра HKLM\ SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\LargeSystemCache. B системах Windows 2000 Server это значение можно было косвенно модифицировать заданием свойств для службы файлового сервера; Windows XP и Windows Server 2003 позволяют сделать это явно: щелкните My Computer (Мой компьютер) правой кнопкой мыши, выберите Properties (Свойства), откройте вкладку Advanced (Дополнительно), нажмите кнопку Settings (Параметры) в разделе Performance (Быстродействие) и перейдите на очередную вкладку Advanced (детали см. в главе 11).
 
База данных PFN
   Если рабочие наборы описывают резидентные страницы, принадлежащие процессу или системе, то база daHHbixPFN(номеров фреймов страниц) определяет состояние каждой страницы в физической памяти. Состояния страниц перечислены в таблице 7-20.
   База данных PFN состоит из массива структур, представляющих каждую страницу физической памяти в системе. Как показано на рис. 7-33, действительные PTE ссылаются на записи базы данных PFN, а эти записи (если они не относятся к прототипным PFN) – на таблицу страниц, которая их использует. Прототипные PFN ссылаются на прототипные РТЕ.
   Страницы, находящиеся в некоторых из перечисленных в таблице 7-20 состояний, организуются в связанные списки, что помогает диспетчеру памяти быстро находить страницы определенного типа. (Активные и переходные страницы не включаются ни в один общесистемный список.) Пример взаимосвязей элементов таких списков в базе данных PFN показан на рис. 7-34.
   B следующем разделе вы узнаете, как эти связанные списки используются при обработке ошибок страниц и как страницы перемещаются между различными списками.
 
    ЭКСПЕРИМЕНТ: просмотр базы данных PFN
   Команда !memusageотладчика ядра позволяет получить информацию о размерах различных списков страниц. Вот пример вывода этой команды.
 
Динамика списков страниц
   Ha рис. 7-35 показана схема состояний фрейма страниц. Для упрощения на ней отсутствует список модифицированных, но не записываемых страниц.
 
   Фреймы страниц перемещаются между различными списками следующим образом.
    (o)Когда диспетчеру памяти нужна обнуленная страница для обслуживания ошибки страницы, обнуляемой по требованию (demand-zero page fault) (ссылки на страницу, которая должна быть заполнена нулями, или на закрытую переданную страницу пользовательского режима, к которой еще не было обращений), он прежде всего пытается получить ее из списка обнуленных страниц. Если этот список пуст, он берет ее из списка свободных страниц и заполняет нулями. Если и этот список пуст, диспетчер памяти извлекает страницу из списка простаивающих страниц и обнуляет ее.
   Одна из причин необходимости обнуления страниц – соответствие требованиям защиты уровня C2: процессам пользовательского режима должны передаваться фреймы обнуленных страниц, чтобы не допустить чтения содержимого памяти предыдущих процессов. Поэтому диспетчер памяти передает процессам пользовательского режима фреймы обнуленных страниц, если только страница не считывается из проецируемого файла. B последнем случае диспетчер памяти использует фреймы необнуленных страниц, инициализируя их данными с диска.
   Список обнуленных страниц заполняется страницами из списка свободных страниц системным потоком обнуления страниц(zero page thread) – это поток 0 процесса System. Он ждет на объекте-событии, который переходит в свободное состояние при наличии в списке свободных страниц восьми и более страниц. Однако этот поток выполняется, только если не работают другие потоки, поскольку он имеет нулевой приоритет, а наименьший приоритет пользовательского потока – 1.
 
    ПРИМЕЧАНИЕ B Windows Server 2003 и более новых версиях, когда возникает необходимость в обнулении памяти из-за выделения физической страницы драйвером, вызвавшим MmAllocatePagesForMdl, или Windows-приложением, вызвавшими AllocateUserPhysicalPages, либо когда приложение выделяет большие страницы, диспетчер памяти обнуляет память через более эффективную функцию MiZeroInParallel Она проецирует регионы большего размера, чем поток обнуления страниц, который выполняет свою операцию только над одной страницей единовременно. Кроме того, в многопроцессорных системах эта функция создает дополнительные системные потоки для параллельного выполнения операций обнуления (с поддержкой специфических оптимизаций на NUMA-платформах).
 
    (o)Если диспетчеру памяти не нужны обнуленные станицы, он сначала обращается к списку свободных страниц и, если тот пуст, переходит к списку простаивающих страниц. Прежде чем диспетчер сможет воспользоваться фреймом страниц из списка простаивающих страниц, он должен проследить ссылку из недействительного PTE (или прототипного РТЕ), который еще ссылается на этот фрейм, и удалить ее. Поскольку элементы (записи) базы данных PFN содержат обратные указатели на таблицу страниц предыдущего пользователя (или на прототипный РТЕ, если страницы разделяемые), диспетчер памяти может быстро найти PTE и внести требуемые изменения.
    (o)Когда процессу приходится отдать страницу из своего рабочего набора (из-за ссылки на новую страницу при заполненном рабочем наборе или из-за усечения рабочего набора, инициированного диспетчером памяти), она переходит в список простаивающих страниц (если ее данные не изменялись) или в список модифицированных (если ее данные изменялись, пока она находилась в памяти). По завершении процесса все его закрытые страницы переходят в список свободных страниц. И еще: как только закрывается последняя ссылка на раздел, поддерживаемый страничным файлом, все его страницы тоже переходят в список свободных страниц.
 
    ЭКСПЕРИМЕНТ: наблюдаем за ошибками страниц
   Утилита Pfmon из ресурсов Windows 2000 и 2003, а также из Windows XP Support Tools позволяет наблюдать за ошибками страниц по мере их возникновения. Ниже показан фрагмент вывода Pfmon при запуске Notepad и его последующем закрытии. Слово SOFT означает, что ошибка страницы была устранена с помощью одного из переходных списков, а слово HARD – что ошибка страницы потребовала чтения с диска. Обратите внимание на итоговые сведения о подкачке страниц в конце листинга.
 
Подсистема записи модифицированных страниц
   При чрезмерном увеличении списка модифицированных страниц либо при уменьшении размера списков обнуленных или простаивающих страниц ниже определенного порогового значения, вычисляемого в ходе загрузки системы и хранящегося в переменной ядра