4.Внутреннее устройство Windows (гл. 12-14)

Руссинович Марк

Соломон Дэвид

 

ГЛABA 12  Файловые системы

B начале этой главы мы даем обзор файловых систем, поддерживаемых Windows, а также описываем типы драйверов файловых систем и принципы их работы, в том числе способы взаимодействия с другими компонентами операционной системы, например с диспетчерами памяти и кэша. Затем поясняем, как пользоваться утилитой Filemon (wwwsysinternals.com) для анализа проблем, связанных с доступом к файловой системе. Мы рассмотрим «родной» для Windows формат файловой системы NTFS и особенности этой файловой системы — сжатие данных, способность к восстановлению, поддержку квот и шифрование.

Чтобы полностью усвоить материал этой главы, вы должны понимать терминологию, введенную в главе 10, в том числе термины «том» и «раздел». Кроме того, вам должны быть знакомы следующие дополнительные термины.

• Секторы — аппаратно адресуемые блоки носителя. Размер секторов на жестких дисках в х86-системах почти всегда равен 512 байтам. Таким образом, если операционная система должна модифицировать 632-й байт диска, она записывает 512-байтовый блок данных во второй сектор диска.

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

• Кластеры — адресуемые блоки, используемые многими файловыми системами. Размер кластера всегда кратен размеру сектора (рис. 12-1). Файловая система использует кластеры для более эффективного управления дисковым пространством: кластеры, размер которых превышает размер сектора, позволяют разбить диск на блоки меньшей длины — управлять такими блоками легче, чем секторами. Потенциальный недостаток кластеров большего размера — менее эффективное использование дискового пространства, или внутренняя фрагментация, которая возникает из-за того, что размеры файлов редко бывают кратны размеру кластера.

• Метаданные — это данные, хранящиеся на томе и необходимые для поддержки управления файловой системой. Как правило, они недоступны приложениям. Метаданные включают, например, информацию, определяющую местонахождение файлов и каталогов на томе.

Файловые системы Windows

Windows поддерживает файловые системы:

• CDFS;

• UDF;

• FAT12, FATl6 и FAT32;

• NTFS.

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

CDFS

CDFS, или файловая система CD-ROM (только для чтения), обслуживается драйвером \Windows\System32\Drivers\Cdfs.sys, который поддерживает надмножества форматов ISO-9660 и Joliet. Если формат ISO-9660 сравнительно прост и имеет ряд ограничений, например имена с буквами верхнего регистра в кодировке ASCII и максимальной длиной в 32 символа, то формат Joliet более гибок и поддерживает Unicode-имена произвольной длины. Если на диске присутствуют структуры для обоих форматов (чтобы обеспечить максимальную совместимость), CDFS использует формат Joilet. CDFS присущ ряд ограничений:

• максимальная длина файлов не должна превышать 4 Гб;

• число каталогов не может превышать 65 535.

CDFS считается унаследованным форматом, поскольку индустрия уже приняла в качестве стандарта для носителей, предназначенных только для чтения, формат Universal Disk Format (UDF).

UDF

Файловая система UDF в Windows является UDF-совместимой реализацией OSTA (Optical Storage Technology Association). (UDF является подмножеством формата ISO-13346 с расширениями для поддержки CD-R, DVD-R/RW и т. д.) OSTA определила UDF в 1995 году как формат магнитооптических носителей, главным образом DVD-ROM, предназначенный для замены формата ISO-9660. UDF включен в спецификацию DVD и более гибок, чем CDFS. Драйвер UDF поддерживает UDF версий 1.02 и 1.5 в Windows 2000, а также версий 2.0 и 2.01 в Windows XP и Windows Server 2003. Файловые системы UDF обладают следующими преимуществами:

• длина имен файлов и каталогов может быть до 254 символов в ASCII-кодировке или до 127 символов в Unicode-кодировке;

• файлы могут быть разреженными (sparse);

• размеры файлов задаются 64-битными значениями.

Хотя формат UDF разрабатывался с учетом особенностей перезаписываемых носителей, драйвер UDF в Windows (\Windows\System32\Drivers\ Udfs.sys) поддерживает носители только для чтения. Кроме того, в Windows не реализована поддержка других возможностей UDF, в частности именованных потоков, списков управления доступом и расширенных атрибутов.

FAT12, FAT16 и FAT32

Windows поддерживает файловую систему FAT по трем причинам: для возможности обновления операционной системы с прежних версий Windows до современных, для совместимости с другими операционными системами при многовариантной загрузке и как формат гибких дисков. Драйвер файловой системы FAT в Windows реализован в \Windows\System32\Drivers\ Fastfat.sys.

B название каждого формата FAT входит число, которое указывает разрядность, применяемую для идентификации кластеров на диске. 12-разрядный идентификатор кластеров в FAT12 ограничивает размер дискового раздела 212 (4096) кластерами. B Windows используются кластеры размером от 512 байтов до 8 Кб, так что размер тома FAT12 ограничен 32 Мб. Поэтому Windows использует FAT12 как формат 5- и 3,5-дюймовых дискет, способных хранить до 1,44 Мб данных.

ПРИМЕЧАНИЕ Все файловые системы FAT резервируют на томе первые два кластера и последние 16, так что число доступных для использования кластеров на томе, например FAT12, чуть меньше 4096.

FATl6 — за счет 16-разрядных идентификаторов кластеров — может адресовать до 216 (65 536) кластеров. B Windows размер кластера FATl6 варьируется от 512 байтов до 64 Кб, поэтому размер FATl6-TOMa ограничен 4 Гб. Размер кластеров, используемых Windows, зависит от размера тома (таблица 12-1). Если вы форматируете том размером менее 16 Мб для FAT с помощью команды format или оснастки Disk Management (Увправление дисками), Windows вместо FATl6 использует FAT12.

Том FAT делится на несколько областей (рис. 12-2). Таблица размещения файлов (file allocation table, FAT), от которой и произошло название файловой системы FAT, имеет по одной записи для каждого кластера тома. Поскольку таблица размещения файлов критична для успешной интерпретации содержимого тома, FAT поддерживает две копии этой таблицы. Так что, если драйвер файловой системы или программа проверки целостности диска (вроде Chkdsk) не сумеет получить доступ к одной из копий FAT (например, из-за плохого сектора на диске), она сможет использовать вторую копию.

Записи в таблице FAT определяют цепочки размещения файлов и каталогов (рис. 12-3), где отдельные звенья представляют собой указатели на следующий кластер с данными файла. Элемент каталога для файла хранит начальный кластер файла. Последний элемент цепочки размещения файла содержит зарезервированное значение 0xFFFF для FATl6 и 0xFFF для FATl 2. Записи FAT, описывающие свободные кластеры, содержат нулевые значения. Ha рис. 12-3 показан файл FILE1, которому назначены кластеры 2, 3 и 4; FILE2 фрагментирован и использует кластеры 5, 6 и 8; a FILE3 занимает только кластер 7. Чтение файла с FAT-тома может потребовать просмотра больших блоков таблицы размещения файлов для поиска всех его цепочек размещения.

Элементы каталога для файлов

B начале тома FAT12 или FATl6 заранее выделяется место для корневого каталога, достаточное для хранения 256 записей (элементов), что ограничивает число файлов и каталогов в корневом каталоге (в FAT32 такого ограничения нет). Элемент каталога FAT, размер которого составляет 32 байта, хранит имя файла, его размер, начальный кластер и метку времени (время создания, последнего доступа и т. д.). Если имя файла состоит из Unicode-символов или не соответствует правилам именования по формуле «8.3», принятым в MS-DOS, оно считается длинным и для его хранения выделяются дополнительные элементы каталога. Вспомогательные элементы предшествуют главному элементу для файла. Ha рис. 12-4 показан пример элемента каталога для файла с именем «The quick brown fox». Система создала представление этого имени в формате «8.3», THEQUI~l.FOX (в элементе каталога вы не увидите «.», поскольку предполагается, что точка следует после восьмого символа), и использовала два дополнительных элемента для хранения длинного Unicode-имени. Каждая строка на рис. 12-4 состоит из 16 байтов.

FAT32 — более новая файловая система на основе формата FAT; она поддерживается Windows 95 OSR2, Windows 98 и Windows Millennium Edition. FAT32 использует 32-разрядные идентификаторы кластеров, но при этом резервирует старшие 4 бита, так что эффективный размер идентификатора кластера составляет 28 бит. Поскольку максимальный размер кластеров FAT32 равен 32 Кб, теоретически FAT32 может работать с 8-терабайтными томами. Windows ограничивает размер новых томов FAT32 до 32 Гб, хотя поддерживает существующие тома FAT32 большего размера (созданные в других операционных системах). Большее число кластеров, поддерживаемое FAT32, позволяет ей управлять дисками более эффективно, чем FATl6. FAT32 может использовать 512-байтовые кластеры для томов размером до 128 Мб. Размеры кластеров на томах FAT32 по умолчанию показаны в таблице 12-2.

Помимо большего предельного числа кластеров преимуществом FAT32 перед FAT12 и FATl6 является тот факт, что место хранения корневого каталога FAT32 не ограничено предопределенной областью тома, поэтому его размер не ограничен. Кроме того, для большей надежности FAT32 хранит вторую копию загрузочного сектора*. B FAT32, как и в FATl6, максимальный размер файла равен 4 Гб, посколькудлина файла в каталоге описывается 32-битным числом.

ПРИМЕЧАНИЕ B Windows XP введена поддержка FAT32 на устройствах DVD-RAM.

NTFS

Как мы уже говорили в начале главы, NTFS — встроенная («родная») файловая система Windows. NTFS использует 64-разрядные номера кластеров. Это позволяет NTFS адресовать тома размером до 16 экзабайт (16 миллиардов Гб). Однако Windows ограничивает размеры томов NTFS до значений, при которых возможна адресация 32-разрядными кластерами, т. е. до 128 Тб (с использованием кластеров по 64 Кб). B таблице 12-3 перечислены размеры кластеров на томах NTFS по умолчанию (эти значения можно изменить при форматировании тома NTFS).

NTFS поддерживает ряд дополнительных возможностей — защиту файлов и каталогов, дисковые квоты, сжатие файлов, символьные ссылки на основе каталогов и шифрование. Одно из важнейших свойств NTFS — восстанавливаемость. При неожиданной остановке системы целостность метаданных тома FAT может быть утрачена, что вызовет повреждение структуры каталогов и значительного объема данных. NTFS ведет журнал изменений метаданных путем протоколирования транзакций, поэтому целостность структур файловой системы может быть восстановлена без потери информации о структуре файлов или каталогов. (Однако данные файлов могут быть потеряны.)

* Точнее — загрузочной записи, которая включает несколько секторов. Подробную информацию о FAT32 см. в книге «Ресурсы Microsoft Windows 98». — Прим. перев.

Подробнее о структурах данных и дополнительных возможностях NTFS мы поговорим позже.

Архитектура драйвера файловой системы

Драйвер файловой системы (file system driver, FSD) управляет форматом файловой системы. Хотя FSD выполняются в режиме ядра, у них есть целый ряд особенностей по сравнению со стандартными драйверами режима ядра. Возможно, самой важной особенностью является то, что они должны регистрироваться у диспетчера ввода-вывода и более интенсивно взаимодействовать с ним. Кроме того, для большей производительности FSD обычно полагаются на сервисы диспетчера кэша. Таким образом, FSD используют более широкий набор функций, экспортируемых Ntoskrnl, чем стандартные драйверы. Если для создания стандартных драйверов режима ядра требуется Windows DDK, то для создания драйверов файловых систем понадобится Windows Installable File System (IFS) Kit (подробнее o DDK см. главу 1; подробнее о IFS Kit см. www.microsoft.com/whdc/devtools/ifskiP). B Windows два типа драйверов файловых систем:

• локальные FSD, управляющие дисковыми томами, подключенными непосредственно к компьютеру;

• сетевые FSD, позволяющие обращаться к дисковым томам, подключенным к удаленным компьютерам.

Локальные FSD

K локальным FSD относятся Ntfs.sys, Fastfat.sys, Udfs.sys, Cdfs.sys и Raw FSD (интегрированный в Ntoskrnl.exe). Ha рис. 12-5 показана упрощенная схема взаимодействия локальных FSD с диспетчером ввода-вывода и драйверами устройств внешней памяти. Как мы поясняли в разделе «Монтирование томов» главы 10, локальный FSD должен зарегистрироваться у диспетчера ввода-вывода. После регистрации FSD диспетчер ввода-вывода может вызывать его для распознавания томов при первом обращении к ним системы или одного из приложений. Процесс распознавания включает анализ загрузочного сектора тома и, как правило, метаданных файловой системы для проверки ее целостности.

Все поддерживаемые Windows файловые системы резервируют первый сектор тома как загрузочный. Загрузочный сектор содержит достаточно информации, чтобы FSD мог идентифицировать свой формат файловой системы тома и найти любые метаданные, хранящиеся на этом томе.

Распознав том, FSD создает объект «устройство», представляющий смонтированную файловую систему. Диспетчер ввода-вывода связывает объект «устройство» тома, созданный драйвером устройства внешней памяти (далее — объект тома), с объектом «устройство», созданным FSD (далее — объект FSD), через блок параметров тома (VPB). Это приводит к тому, что диспетчер ввода-вывода перенаправляет через VPB запросы ввода-вывода, адресованные объекту тома, на объект FSD (подробнее о VPB см. главу 10).

Рис. 12-5. Локальный FSD

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

Локальные FSD также поддерживают операции демонтирования файловой системы, позволяющие операционной системе отсоединять FSD от объекта тома. Демонтирование происходит каждый раз, когда приложение напрямую обращается к содержимому тома или когда происходит смена носителя, сопоставленного с томом. При первом обращении приложения к носителю после демонтирования диспетчер ввода-вывода повторно инициирует операцию монтирования тома для этого носителя.

Удаленные FSD

Удаленные FSD состоят из двух компонентов: клиента и сервера. Удаленный FSD на клиентской стороне позволяет приложениям обращаться к удаленным файлам и каталогам. Клиентский FSD принимает запросы ввода-вывода от приложений и транслирует их в команды протокола сетевой файловой системы, посылаемые через сеть компоненту на серверной стороне, которым обычно является удаленный FSD. Серверный FSD принимает команды, поступающие по сетевому соединению, и выполняет их. При этом он выдает запросы на ввод-вывод локальному FSD, управляющему томом, на котором расположен нужный файл или каталог.

Windows включает клиентский удаленный FSD, LANMan Redirector (редиректор), и серверный удаленный FSD, LANMan Server (сервер) (\Windows\ System32\Drivers\Srv.sys). Редиректор реализован в виде комбинации порт- и минипорт-драйверов, где порт-драйвер (\Windows\System32\Drivers \Rdbss.sys) представляет собой библиотеку подпрограмм, а минипорт-драйвер (\Windows\System32\Drivers\Mrxsmb.sys) использует сервисы, реализуемые порт-драйвером. Еще один минипорт-драйвер редиректора — WebDAV (\Win-dows\System32\Drivers\Mrxdav.sys), который реализует клиентскую часть поддержки доступа к файлам по HTTR Модель «порт-минипорт» упрощает разработку редиректора, потому что порт-драйвер, совместно используемый всеми минипорт-драйверами удаленных FSD, берет на себя многие рутинные операции, требуемые при взаимодействии между клиентским FSD и диспетчером ввода-вывода Windows. B дополнение к FSD-компонентам LANMan Redirector и LANMan Server включают Windows-службы рабочей станции и сервера соответственно. Взаимодействие между клиентом и сервером при доступе к файлам на серверной стороне через редиректор и серверные FSD показано на рис. 12-6.

Для форматирования сообщений, которыми обмениваются редиректор и сервер, Windows использует протокол CIFS (Common Internet File System). CIFS — это версия протокола Microsoft SMB (Server Message Block). (Подробнее o CIFS см. книгу «Сети TCP/IP. Ресурсы Microsoft Windows 2000 Server» и сайт www.cifs.com.)

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

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

• Level I oplock предоставляется при монопольном доступе клиента к файлу Клиент, удерживающий для файла oplock этого типа, может кэшировать операции как чтения, так и записи.

• Level II oplock — разделяемая блокировка файла. Клиенты, удерживающие oplock этого типа, могут кэшировать операции чтения, но запись в файл делает Level II oplock недействительным.

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

B отсутствие oplock клиент не может осуществлять локальное кэширование ни операций чтения, ни операций записи; вместо этого он должен получать данные с сервера и посылать все изменения непосредственно на сервер.

Проиллюстрировать работу oplock поможет пример на рис. 12-7. Первому клиенту, открывающему файл, сервер автоматически предоставляет Level I oplock. Редиректор на клиентской стороне кэширует файловые данные при чтении и записи в кэше файловой системы локальной машины. Если тот же файл открывает второй клиент, он также запрашивает Level I oplock. Теперь уже два клиента обращаются к одному и тому же файлу, поэтому сервер должен принять меры для согласования представления данных файла обоим клиентам. Если первый клиент произвел запись в файл (этот случай и показан на рис. 12-7), сервер отзывает oplock и больше не предоставляет его ни одному клиенту. После отзыва oplock первый клиент сбрасывает все кэшированные данные файла обратно на сервер.

Если бы первый клиент не произвел запись, его oplock был бы понижен до Level II oplock, т. е. до oplock того же типа, который сервер предоставляет второму клиенту. B этом случае оба клиента могли бы кэшировать операции чтения, но после операции записи любым из клиентов сервер отозвал бы их oplock, и последующие операции были бы некэшируемыми. Однажды отозванный, oplock больше не предоставляется для этого экземпляра открытого файла. Однако, если клиент закрывает файл и повторно открывает его, сервер заново решает, какой oplock следует предоставить клиенту. Решение сервера зависит от того, открыт ли файл другими клиентами и производил ли хоть один из них запись в этот файл.

ЭКСПЕРИМЕНТ: просмотр списка зарегистрированных файловых систем

Диспетчер ввода-вывода, загружая в память драйвер устройства, обычно присваивает имя объекту «драйвер», который создается для представления этого драйвера. Этот объект помещается в каталог \Drivers диспетчера объектов. Объекты «драйвер» любого загружаемого диспетчером ввода-вывода драйвера с атрибутом Туре, равным SERVICEFILE _SYSTEM_DRIVER (2), помещаются этим диспетчером в каталог \File-System. Таким образом, утилита типа Winobj (wwwsysintemals.com) позволяет увидеть зарегистрированные файловые системы, как показано на следующей иллюстрации. (Заметьте, что некоторые драйверы файловых систем помещают в каталог \FileSystem и объекты «устройство».)

Еще один способ увидеть зарегистрированные файловые системы — запустить программу System Information (Сведения о системе). B Windows 2000 запустите оснастку Computer Management (Управление компьютером) и выберите Drivers (Драйверы) в Software Environment (Программная среда) в узле System Information (Сведения о системе); в Windows XP и Windows Server 2003 запустите Msinfo32 и выберите System Drivers (Системные драйверы) в узле Software Environment (Программная среда). Отсортируйте список драйверов, щелкнув колонку Туре (Тип), и драйверы с атрибутом SERVICE_FILE_SYSTEM_DRIVER окажутся в начале списка.

Заметьте: если драйвер регистрируется как SERVICE_FILE_SYSTEM_DRIVER, это еще не означает, что он служит локальным или удаленным FSD. Например, Npfs (Named Pipe File System), присутствующий на только что показанной иллюстрации, на самом деле является драйвером сетевого API — он поддерживает именованные каналы, но реализует закрытое пространство имен и поэтому в какой-то мере подобен драйверу файловой системы. Пространство имен Npfs исследуется в одном из экспериментов в главе 13.

Работа файловой системы

Система и приложения могут обращаться к файлам двумя способами: напрямую (через функции ввода-вывода вроде ReadFile и WriteFile) и косвенно, путем чтения или записи части своего адресного пространства, где находится раздел проецируемого файла (подробнее о проецируемых файлах см. главу 7). Упрощенная схема на рис. 12-8 иллюстрирует компоненты, участвующие в работе файловой системы, и способы их взаимодействия. Как видите, есть несколько путей вызова FSD:

• из пользовательского или системного потока, выполняющего явную операцию файлового ввода-вывода;

• из подсистем записи модифицированных и спроецированных страниц, принадлежащих диспетчеру памяти;

• неявно из подсистемы отложенной записи, принадлежащей диспетчеру кэша;

• неявно из потока опережающего чтения, принадлежащего диспетчеру кэша;

• из обработчика ошибок страниц, принадлежащего диспетчеру памяти.

Явный файловый ввод-вывод

Наиболее очевидный способ доступа приложения к файлам — вызов Windows-функций ввода-вывода, например CreateFile, ReadFile и WriteFile. Приложение открывает файл с помощью CreateFile, а затем читает, записывает и удаляет его, передавая описатель файла, возвращенный CreateFile, другим Windows-функциям. CreateFile, реализованная в Kernel32.dll, вызывает встроенную функцию NtCreateFile и формирует полное имя файла, обрабатывая символы «.» и «…» и предваряя путь строкой «\??» (например, \??\C: \Daryl\Todo.txt).

Чтобы открыть файл, системный сервис NtCreateFile вызывает функцию ObOpenObjectByName, которая выполняет разбор имени, начиная с корневого каталога диспетчера объектов и первого компонента полного имени («??»). B главе 3 дано подробное описание разрешения имен диспетчером объектов, а здесь мы поясним, как происходит поиск букв диска для томов.

Первое, что делает диспетчер объектов, — транслирует \?? в каталог пространства имен, индивидуальный для сеанса, в котором выполняется данный процесс; на этот каталог ссылается поле DosDevicesDirectory в структуре карты устройств (device map structure) в объекте «процесс». B системах Windows 2000 без Terminal Services поле DosDevicesDirectory ссылается на каталог \?? а в системах Windows 2000 без Terminal Services карта устройств ссылается на индивидуальный для каждого сеанса каталог, где хранятся объекты «символьная ссылка», представляющие все действительные буквы дисков для томов. Однако в Windows XP и Windows Server 2003 в таком каталоге обычно содержатся лишь имена томов для общих сетевых ресурсов, поэтому в этих OC диспетчер объектов, не найдя имя (в данном примере — G) в индивидуальном для сеанса каталоге, переходит к поиску в каталоге, на который ссылается поле GlobalDosDevicesDirectory карты устройств, сопоставленной с индивидуальным для сеанса каталогом. GlobalDosDevicesDirectory всегда указывает на каталог \Global?? где Windows XP и Windows Server 2003 хранят буквы дисков для локальных томов. (Подробнее о пространстве имен сеанса см. одноименный раздел в главе 3-)

Символьная ссылка для буквы диска, присвоенной тому, указывает на объект тома в каталоге \Device, поэтому диспетчер объектов, распознав объект тома, передает остаток строки с именем в функцию IopParseDevice, зарегистрированную диспетчером ввода-вывода для объектов «устройство». (Ha томах динамических дисков символьная ссылка указывает на промежуточную ссылку, которая в свою очередь указывает на объект тома.) Ha рис. 12-9 показано, как происходит доступ к объектам томов через пространство имен диспетчера объектов. B данном случае (система Windows 2000 без Terminal Services) символьная ссылка \??\C: указывает на объект тома \Device\HarddiskVolumel.

Заблокировав контекст защиты вызывающего потока и получив информацию о защите из его маркера, IopParseDevice генерирует пакет запроса ввода-вывода (IRP) типа IRP_MJ_CREATE, создает объект «файл», в котором запоминается имя открываемого файла, и по ссылке в VPB объекта тома находит объект «устройство» смонтированной файловой системы тома. Далее, используя IoCallDriver, она передает IRP драйверу файловой системы, которому принадлежит данный объект «устройство».

Когда FSD получает IRP типа IRP_MJ_CREATE, он ищет указанный файл, проверяет права доступа и, если файл есть и пользователь имеет права на запрошенный вид доступа к файлу, возвращает код успешного завершения. Диспетчер объектов создает в таблице описателей, принадлежащей процессу, описатель объекта «файл», который передается назад по цепочке вызовов, в конечном счете достигая приложения в виде параметра, возвращаемого CreateFile. Если файловой системе не удается создать файл, диспетчер ввода-вывода удаляет созданный для него объект «файл».

Мы опустили здесь детали, относящиеся к тому, как FSD находит открываемый на томе файл. B выполнении ReadFile ядро участвует в той же мере, что и в выполнении CreateFile, но в этом случае системному cepвucy NtReadFiIe не приходится искать имя — он вызывает диспетчер объектов для трансляции описателя, переданного ReadFile, в указатель на объект «файл». Если описатель открытого файла указывает на наличие у вызывающего потока прав на чтение файла, NtReadFile создает IRP типа IRPMJREAD и посылает его драйверу файловой системы, в которой находится файл. NtReadFile получает объект FSD, хранящийся в объекте «файл», и вызывает IoCallDriver. Диспетчер ввода-вывода находит FSD с помощью объекта FSD и передает IRP этому драйверу файловой системы.

Если считываемый файл может быть кэширован (т. е. при открытии файла в функцию CreateFile не передан флаг FILE_FLAG_NO_BUFFERING), FSD проверяет, инициировано ли кэширование для этого объекта «файл». Если да, поле PrivateCacheMap объекта «файл» указывает на структуру закрытой карты кэша (см. главу 11). B ином случае поле PrivateCacheMap будет пустым. Кэширование объекта «файл» инициируется FSD при первой операции записи или чтения над этим объектом, для чего FSD вызывает функцию CcIni-tializeCacheMap диспетчера кэша, и диспетчер кэша создает закрытую и общую карты кэша, а также объект «раздел» (если это еще не сделано).

Убедившись, что кэширование файла разрешено, FSD копирует данные запрошенного файла из виртуальной памяти диспетчера кэша в буфер, указатель на который передан функции ReadFile вызывающим потоком. Файловая система выполняет копирование в рамках блока try/except, что позволяет перехватывать все ошибки, которые могут возникнуть, если приложение указало неверный буфер. Для копирования файловая система использует функцию CcCopyRead диспетчера кэша, которая принимает в качестве параметров объект «файл», смещение внутри файла и длину данных.

Диспетчер кэша, выполняя CcCopyRead, получает указатель на общую карту кэша, хранящуюся в объекте «файл». Вспомните из главы 11, что эта карта хранит указатели на блоки управления виртуальными адресами (VACB) и что один элемент VACB соответствует 256-килобайтному блоку файла. Если VACB-указатель для считываемой части файла пуст, CcCopyRead создает VACB, резервируя в виртуальном адресном пространстве диспетчера кэша 256-ки-лобайтное представление, и проецирует на это представление указанную порцию файла (с помощью MmMapViewInSystemCacbe). Затем CcCopyRead просто копирует данные файла из спроецированного представления в переданный ей буфер (буфер, изначально переданный в ReadFile). Если файловых данных в физической памяти нет, операция копирования вызывает ошибки страниц, обслуживаемые MmAccessFault.

Когда возникает ошибка страницы, MmAccessFault изучает виртуальный адрес, вызвавший ошибку, и находит дескриптор виртуального адреса (VAD) в дереве VAD вызвавшего ошибку процесса (подробнее о дереве VAD см. главу 7). B данном случае VAD описывает представление считываемого файла, проецируемое диспетчером кэша, поэтому для обработки ошибки страницы, вызванной действительным виртуальным адресом, MmAccessFault вызывает MiDispatcbFault, которая сначала находит область управления (на нее указывает VAD) и уже через нее отыскивает объект «файл», представляющий открытый файл. (Если файл открывался более чем один раз, возможно наличие списка объектов «файл», связанных указателями в закрытых картах кэша.)

Найдя объект «файл», MiDispatcbFault вызывает функцию IoPageRead диспетчера ввода-вывода, чтобы создать IRP (типа IRP_MJ_READ), и посылает этот IRP к FSD, владеющему объектом «устройство», на который указывает объект «файл». Таким образом, осуществляется повторный вход в файловую систему для чтения данных, запрошенных через CcCopyRead, но на этот раз в IRP присутствует флаг, который сообщает о необходимости некэшируемого и связанного с подкачкой ввода-вывода. Этот флаг сигнализирует FSD, что он должен извлечь данные непосредственно с диска, и тот так и поступает, определяя, какие кластеры диска содержат запрошенные данные, и посылая соответствующие IRP диспетчеру томов, владеющему объектом тома, на котором находится файл. Поле блока параметров тома (VPB) объекта FSD указывает на объект тома.

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

WriteFile работает аналогичным образом с тем исключением, что системный сервис NtWriteFile генерирует IRP типа IRP_MJ_WRITE, a FSD вызывает не CcCopyRead, a CcCopyWrite. Последняя, как и CcCopyRead, проверяет, спроецированы ли на кэш части записываемого файла, и копирует в кэш содержимое буфера, переданного в WriteFile.

Если файловые данные уже хранятся в системном рабочем наборе, вышеописанный сценарий немного меняется. Если файловые данные находятся в кэше, CcCopyRead не вызывает ошибки страниц. Кроме того, в определенных обстоятельствах NtReadFile и NtWriteFile — вместо того чтобы немедленно создать IRP и послать его FSD — вызывают в FSD точку входа для быстрого ввода-вывода. Вот некоторые из этих обстоятельств: считываемая часть файла должна находиться в первых 4 Гб файла, в файле не должно быть блокировок, а считываемая или записываемая часть файла не должна выходить за пределы его текущей длины.

B большинстве FSD точки быстрого ввода-вывода вызывают в диспетчере кэша функции CcFastCopyRead и CcFastCopyWrite для чтения и записи. Эти варианты стандартных процедур копирования требуют, чтобы перед копированием файловые данные были спроецированы на кэш файловой системы. Если это условие не выполнено, CcFastCopyRead и CcFastCopyWrite сообщают о невозможности быстрого ввода-вывода; тогда функции NtReadFile и NtWriteFile возвращаются к созданию IRP (Более полное описание быстрого ввода-вывода см. в разделе «Быстрый ввод-вывод» главы 11.)

Подсистемы записи модифицированных и спроецированных страниц

Для сброса модифицированных страниц при нехватке доступной памяти периодически пробуждаются потоки принадлежащих диспетчеру памяти подсистем записи модифицированных и спроецированных страниц. Эти потоки вызывают функцию IoSyncbronousPageWrite, чтобы создать IRP типа IRP_MJ_WRITE и записать страницы либо в страничный файл, либо в файл, модифицированный после того, как он был спроецирован. Как и в IRP, создаваемом MiDispatchFault, в этих IRP устанавливается флаг некэшируемого и связанного с подкачкой ввода-вывода. Поэтому для записи содержимого памяти на диск FSD обходит кэш файловой системы и выдает IRP непосредственно драйверу устройства внешней памяти.

Подсистема отложенной записи

Поток подсистемы отложенной записи, принадлежащей диспетчеру кэша, тоже участвует в записи модифицированных страниц, поскольку периодически сбрасывает на диск измененные представления разделов файлов, проецируемых на кэш. Операция сброса, выполняемая диспетчером кэша вызовом MmFlushSection, заставляет диспетчер памяти записать на диск все модифицированные страницы в сбрасываемой части раздела. Как и подсистемы записи модифицированных и спроецированных страниц, MmFlusbSection посылает данные FSD через IoSyncbronousPageWrite.

Поток, выполняющий опережающее чтение

Диспетчер кэша включает поток, отвечающий за попытку чтения данных из файлов до того, как их явным образом запросит приложение, драйвер или системный поток. Чтобы определить объем подлежащих чтению данных, поток опережающего чтения использует хронологию операций чтения, которая хранится в закрытой карте кэша объекта «файл». Выполняя опережающее чтение, этот поток просто проецирует на кэш ту часть файла, которую он хочет считать (при необходимости создавая VACB), и обращается к спроецированным данным. Если при попытках обращения возникают ошибки страниц, активизируется обработчик ошибок страниц, который подгружает нужные страницы в системный рабочий набор.

Обработчик ошибок страниц

Мы описывали использование обработчика ошибок страниц в контексте явного файлового ввода-вывода и опережающего чтения диспетчера кэша. Ho этот обработчик активизируется и всякий раз, когда приложение обращается к виртуальной памяти, являющейся представлением проецируемого файла, и встречает страницы, которые представляют часть файла, но не входят в рабочий набор приложения. Обработчик MmAccessFault диспетчера памяти предпринимает те же действия, что и при генерации ошибок страниц в результате выполнения CcCopyRead или CcCopyWrite, и через IoPage-Read посылает соответствующие IRP файловой системе, в которой хранится нужный файл.

Драйверы фильтров файловой системы

Драйвер фильтра, занимающий в иерархии более высокий уровень, чем драйвер файловой системы, называется драйвером фильтра файловой системы (file system filter driver). (O драйверах фильтров см. главу 9.) Его способность видеть все запросы к файловой системе и при необходимости модифицировать или выполнять их, делает возможным создание таких приложений, как службы репликации удаленных файлов, шифрования файлов, резервного копирования и лицензирования. B любой коммерческий антивирусный сканер, проверяющий файлы на «лету», входит драйвер файловой системы, который перехватывает IRP-пакеты с командами IRP_MJ_CREATE, выдаваемыми при каждом открытии файла приложением. Прежде чем передать такой IRP драйверу файловой системы, которому адресована данная команда, антивирусный сканер проверяет открываемый файл на наличие вирусов. Если файл чист, антивирусный сканер передает IRP дальше по цепочке, но если файл заражен, сканер обращается к своему сервисному процессу для удаления или лечения этого файла. Если вылечить файл нельзя, драйвер фильтра отклоняет IRP (обычно с ошибкой «доступ запрещен»), чтобы вирус не смог активизироваться.

B этом разделе мы опишем работу двух специфических драйверов фильтров файловой системы: Filemon и System Restore. Filemon — утилита для мониторинга активности файловой системы (с сайта wwwsystntemals.com), используемая во многих экспериментах в этой книге, — является примером пассивного драйвера фильтра, который не модифицирует поток IRP между приложениями и драйверами файловой системы. System Restore (Восстановление системы) — функциональность, введенная в Windows XP, — использует драйвер фильтра файловой системы для наблюдения за изменениями в ключевых системных файлах и создает их резервные копии, чтобы эти файлы можно было возвращать в те состояния, которые были у них в моменты создания точек восстановления.

ПРИМЕЧАНИЕ B Windows XP Service Pack 2 и Windows Server 2003 включен Filesystem Filter Manager (\Windows\System32\Drivers\Fltmgr. sys) как часть модели «порт-минипорт» для драйверов фильтров файловой системы. Этот компонент будет доступен и для Windows 2000. Filesystem Filter Manager кардинально упрощает разработку драйверов фильтров, предоставляя интерфейс минипорт-драйверов фильтров к подсистеме ввода-вывода Windows, а также поддерживая сервисы для запроса имен файлов, подключения к томам и взаимодействия с другими фильтрами. Компании-разработчики, в том числе Microsoft, будут писать новые фильтры файловых систем на основе инфраструктуры, предоставляемой Filesystem Filter Manager, и переносить на нее существующие фильтры.

Filemon

Утилита Filemon извлекает драйвер устройства «фильтр файловой системы» (Filem.sys) из своего исполняемого образа (Filemon.exe) при первом ее запуске после загрузки, устанавливает этот драйвер в памяти, а затем удаляет его образ с диска. Через GUI утилиты Filemon вы можете указывать ей вести мониторинг активности файловой системы на локальных томах, общих сетевых ресурсах, именованных каналах и почтовых ящиках (mail slots). Получив команду начать мониторинг тома, драйвер создает объект «устройство» фильтра и подключает его к объекту «устройству», который представляет смонтированную файловую систему на томе. Например, если драйвер NTFS смонтировал том, драйвер Filemon подключит к объекту «устройство» данного тома свой объект «устройство», используя функцию IoAttacbDeviceToDeviceStackSafe диспетчера ввода-вывода. После этого IRP, адресованный нижележащему объекту «устройство», перенаправляется диспетчером ввода-вывода драйверу, которому принадлежит подключенный объект «устройство» (в данном случае — Filemon).

Перехватив IRP, драйвер Filemon записывает информацию о команде в этом IRP, в том числе имя целевого файла и другие параметры, специфичные для команды (например, размер и смещение считываемых или записываемых данных), в буфер режима ядра, созданный в неподкачиваемой памяти. Дважды в секунду Filemon GUI посылает IRP объекту «устройство» интерфейса Filemon, который запрашивает копию буфера, содержащего сведения о самых последних операциях, и отображает их в окне вывода. Применение Filemon подробно описывается в разделе «Анализ проблем в файловой системе» далее в этой главе.

System Restore

System Restore (Восстановление системы) — сервис, в рудиментарной форме впервые появившийся в Windows Me (Millennium Edition), позволяет восстанавливать систему до предыдущего известного состояния в ситуациях, в которых иначе пришлось бы переустанавливать какое-то приложение или даже всю операционную систему. (System Restore нет в Windows 2000 и Windows Server 2003) Например, если вы установили одно или несколько приложений или внесли какие-то изменения в реестр либо системный файл и это вызвало проблемы в работе приложений или крах системы, вы можете использовать System Restore для отката системных файлов и реестра в предыдущее состояние. System Restore особенно полезен, когда вы устанавливаете приложение, вносящее нежелательные изменения в системные файлы. Программы установки, совместимые с Windows XP, интегрируются с System Restore и создают точку восстановления до начала процесса установки.

Ядро System Restore содержится в сервисе SrService, который вызывается из DLL (\Windows\System32\Srsvc.dll), выполняемой в экземпляре универсального процесса — хоста сервисов (\Windows\System32\Svchost.exe). (Описание Svchost см. в главе 4.) Роль этого сервиса заключается как в автоматическом создании точек восстановления, так и в экспорте соответствующего API другим приложениям, например программам установки. (Кстати, вы можете вручную инициировать создание точки восстановления.) System Restore считывает свои конфигурационные параметры из раздела реестра HKLM\System\ CurrentControlSet\Services\SR\Parameters, в том числе указывающие, сколько места на диске должно быть свободно для работы этого сервиса и через какой интервал следует автоматически создавать точки восстановления. По умолчанию данный сервис создает точку восстановления перед установкой неподписанного драйвера устройства и через каждые 24 часа работы системы. Если в упомянутом выше разделе реестра задан DWORD-параметр RPGIobalInterval, он переопределяет этот интервал и задает минимальное время в секундах между моментами автоматического создания точек восстановления.

Создавая новую точку восстановления, сервис System Restore сначала создает каталог для этой точки, затем делает снимок состояния критически важных системных файлов, включая кусты реестра, относящиеся к системе и пользовательским профилям, конфигурационную информацию WMI, файл метабазы IIS (если IIS установлена) и регистрационную базу данных СОМ. Потом драйвер восстановления системы, \Windows\System32\Drivers \Sr.sys, начинает отслеживать изменения в файлах и каталогах, сохраняя копии удаляемых или модифицируемых файлов в точке восстановления, а также отмечая другие изменения, например создание и удаление каталогов, в журнале регистрации изменений.

Данные точки восстановления поддерживаются для каждого тома индивидуально, поэтому журналы изменений и сохраненные файлы хранятся в каталогах \System Volume Information\_restore{XX–XXX–XXX), где символы X представляют назначенный системой GUID соответствующего тома. Такой каталог содержит подкаталоги точек восстановления с именами в виде RPn, где n — уникальный идентификатор точки восстановления. Файлы, входящие в начальный снимок точки восстановления, хранятся в каталоге Snapshot точки восстановления.

Резервным копиям файлов, созданным драйвером System Restore, присваиваются уникальные имена (вроде A0000135.dll), и эти файлы помещаются в соответствующий каталог. C точкой восстановления может быть сопоставлено несколько журналов изменений, каждый из которых получает имя в виде Change.log.N, где N — уникальный идентификатор журнала изменений. B каждой записи этого журнала содержатся данные, которые позволяют отменить изменения, внесенные в какой-либо файл или каталог. Например, если вы удалите файл, то соответствующая этой операции запись будет хранить имя его копии в точке восстановления (допустим, A0000135.dll), а также исходные длинное и краткое имена этого файла. Сервис System Restore создает новый журнал изменений, когда размер текущего достигает 1 Мб. Рис. 12–10 отражает ход обработки запросов к файловой системе в то время, как драйвер System Restore обновляет точку восстановления, реагируя на какие-либо изменения.

Рис. 12–11 иллюстрирует содержимое каталога, созданного System Restore и включающего несколько подкаталогов точек восстановления, а также содержимое подкаталога, который соответствует точке восстановления 1. Заметьте, что каталоги \System Volume Information недоступны ни по пользовательской учетной записи, ни по учетной записи администратора — к ним можно обращаться только по учетной записи Local System. Чтобы увидеть этот каталог, используйте утилиту PsExec с сайта wwwsysinternals.com, как показано ниже:

Попав в каталог System Restore, вы можете просмотреть его содержимое командой DIR или перейти в подкаталоги, сопоставленные с точками восстановления.

Каталог точки восстановления на загрузочном томе также содержит двоичный файл _filelst.cfg, который включает расширения файлов, изменения в которых следует фиксировать в точке восстановления, и список каталогов (хранящих, например, временные файлы), изменения в которых следует игнорировать. Этот список, документированный в Platform SDK, указывает System Restore отслеживать только файлы, отличные от файлов данных. (Вряд ли вам понравится, если при откате системы из-за проблемы с каким-нибудь приложением сервис System Restore удалит важный для вас документ Microsoft Word.)

ЭКСПЕРИМЕНТ: изучение объектов «устройство», принадлежащих фильтру System Restore

Для мониторинга изменений в файлах и каталогах драйвер фильтра System Restore должен подключить объекты «устройство» фильтра к объектам «устройство» FAT и NTFS, представляющим тома. Кроме того, он подключает объект «устройство» фильтра к объектам «устройство», представляющим драйверы файловой системы, чтобы узнавать о монтировании новых томов файловой системой и соответственно подключать к ним объекты «устройство» фильтра. Объекты «устройство» System Restore можно просмотреть с помощью отладчика ядра:

B этом примере вывода у драйвера System Restore три объекта «устройство». Последний из них в списке называется SystemRestore — он служит интерфейсом, которому компоненты System Restore пользовательского режима направляют свои команды:

Первый и второй объекты подключены к объектам «устройство» файловой системы NTFS:

Один из объектов «устройство» NTFS является интерфейсом для драйвера файловой системы NTFS, так как его имя — NTFS:

Другой из них представляет смонтированный NTFS-том на C:, и, поскольку в системе только один том, у этого объекта нет имени:

Когда пользователь сообщает системе о необходимости восстановления, мастер System Restore (\Windows\System32\Restore\Rstrui.exe) создает параметр RestoreInProgress типа DWORD в разделе реестра System Restore и присваивает ему значение 1. Затем он инициирует перезагрузку системы, вызывая Windows-функцию ExitWindowsEx. После перезагрузки процесс Winlogon (\Windows\System32\Winlogon.exe) обнаруживает, что нужно выполнить восстановление, копирует сохраненные файлы в исходные каталоги и с помощью файлов журнала отменяет изменения, внесенные в системные файлы и каталоги. По окончании этого процесса загрузка системы возобновляется. Перезагрузка необходима не только для большей безопасности восстановления, но и для активизации восстановленных кустов реестра.

B Platform SDK документированы две API-функции System Restore, SRSetRestorePoint и SRRemoveRestorePoint, предназначенные для программ установки. Разработчики должны продумать расширения файлов, используемые их приложениями, чтобы они не сохраняли пользовательские данные в файлах тех типов, которые защищаются сервисом System Restore. Иначе пользователь может потерять свои данные при откате системы до точки восстановления.

Анализ проблем в файловой системе

B главе 4 было показано, как система и приложения хранят данные в реестре. Если в реестре появляются какие-то проблемы, скажем, из-за неправильно сконфигурированной защиты или недостающих параметров/разделов реестра, то они могут быть причиной многих сбоев в работе системы и приложений. Помимо этого, система и приложения используют файлы для хранения данных и обращаются к образам DLL и исполняемых файлов. Значит, ошибки в конфигурации защиты NTFS и отсутствие каких-либо файлов или каталогов также являются распространенной причиной сбоев в работе системы и приложений. A все потому, что система и приложения часто полагаются на возможность беспрепятственного доступа к таким файлам и начинают вести себя непредсказуемым образом, если эти файлы оказываются недоступны.

Утилита Filemon отражает все файловые операции по мере их выполнения, что превращает ее в идеальный инструмент для анализа сбоев системы и приложений из-за проблем в файловой системе. Пользовательский интерфейс Filemon практически идентичен таковому в Regmon, и Filemon включает те же средства фильтрации, выделения и поиска, что и Regmon. Первый запуск Filemon в системе требует учетной записи с теми же привилегиями, что и в случае Regmon: Load Driver и Debug. После загрузки драйвер остается резидентным в памяти, поэтому для последующих запусков Filemon достаточно привилегии Debug.

Базовый и расширенный режимы Filemon

При запуске Filemon начинает работу в базовом режиме, в котором отображаются те операции в файловой системе, которые наиболее полезны для анализа проблем. B этом режиме Filemon не показывает определенные операции в файловой системе, в том числе:

• обращения к файлам метаданных NTFS;

• операции в процессе System;

• ввод-вывод, связанный со страничным файлом;

• ввод-вывод, генерируемый процессом Filemon;

• неудачные попытки быстрого ввода-вывода.

Кроме того, в базовом режиме Filemon сообщает об операциях файлового ввода-вывода, используя описательные имена, а не типы IRP, которые на самом деле представляют их. B частности, операции IRP_MJ_WRITE и FASTIO_ WRITE отображаются как Write, а операции IRP_MJ_CREATE — как Open (при открытии существующих файлов) или Create (при создании новых файлов).

ЭКСПЕРИМЕНТ: наблюдение за активностью файловой системы в простаивающей системе

Драйверы файловых систем Windows поддерживают уведомление об изменениях в файлах, что позволяет приложениям узнавать о таких изменениях в файловой системе без постоянного ее опроса. Для этого предназначены Windows-функции ReadDirectoryCbangesW, FindFirst-CbangeNotification и FindNextCbangeNotification. Таким образом, запуская Filemon в простаивающей системе, вы не увидите повторяющихся обращений к файлам и каталогам, поскольку такая активность не обязательно должна негативно отразиться на общей производительности системы.

Запустите Filemon и через несколько секунд проверьте в журнале вывода, есть ли попытки периодического опроса. Обнаружив соответствующую строку, щелкните ее правой кнопкой мыши и выберите из контекстного меню Process Properties для просмотра детальных сведений о процессе, который выполняет операции опроса.

Методики анализа проблем с применением Filemon

Два основных метода анализа проблем с применением Filemon идентичны таковым при использовании Regmon: поиск последней операции в трассировочной информации Filemon перед тем, как в приложении произошел сбой, или сравнение трассировочной информации Filemon для сбойного приложения с аналогичными сведениями для работающей системы. Подробнее об этих способах см. раздел «Методики анализа проблем с применением Regmon» главы 4.

Обращайте внимание на записи в выводе Filemon со значениями FILE NOT FOUND, NO SUCH FILE, PATH NOT FOUND, SHARING VIOLATION и ACCESS DENIED в столбце Result. Первые три значения указывают на то, что приложение или система пытается открыть несуществующий файл или каталог. Bo многих случаях эти ошибки не свидетельствуют о серьезной проблеме. Например, если вы запускаете какую-то программу из диалогового окна Run (Запуск программы), не задавая полный путь к ней, Explorer будет искать эту программу в каталогах, перечисленных в переменной окружения PATH, пока не найдет нужный образ или не закончит просмотр всех перечисленных каталогов. Каждая попытка найти образ в каталоге, где такого образа нет, отражается в выводе Filemon строкой, аналогичной приведенной ниже:

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

Прорехи в защите из-за переполнения буфера представляют серьезную угрозу безопасности, но код результата BUFFER OVERFLOW — просто способ, используемый драйвером файловой системы, чтобы сообщить приложению о нехватке места в выделенном буфере для сохранения полученных данных. Разработчики приложений применяют этот способ, чтобы определить правильный размер буфера, так как драйвер файловой системы заодно сообщает и эту информацию. За операциями с кодом результата BUFFER OVERFLOW обычно следуют операции с успешным результатом.

ЭКСПЕРИМЕНТ: выявление истинной причины ошибки с помощью Filemon

Иногда приложения выводят сообщения об ошибках, которые не раскрывают истинную причину проблемы. Такие сообщения могут запутать и заставить вас тратить время на диагностику несуществующих проблем. Если сообщение об ошибке относится к проблеме в файловой системе, Filemon покажет, какие нижележащие ошибки могли бы предшествовать этому сообщению.

B данном эксперименте вы установите разрешение для каталога, а затем попытаетесь выполнить в нем операцию сохранения файла из Notepad, что приведет к появлению сообщения о совсем другой ошибке. Filemon покажет истинную ошибку и причину вывода в Notepad именно такого сообщения.

1. Запустите Filemon и установите включающий фильтр для «notepad.exe».

2. Откройте Explorer и создайте каталог Test в одном из каталогов NTFS-тома. (B данном примере используется корневой каталог.)

3. Измените разрешения для каталога Test так, чтобы запретить любой вид доступа к нему. He исключено, что вам придется открыть диалоговое окно Advanced Security Settings (Дополнительные параметры безопасности) и задать параметры на вкладке Permissions (Разрешения), чтобы удалить наследуемые разрешения защиты.

Когда вы примените измененные разрешения, Explorer должен будет предупредить вас о том, что никто не получит доступа к этой папке. 4. Запустите Notepad (Блокнот) и введите в нем какой-нибудь текст.

Потом выберите команду Save (Сохранить) из меню FiIe (Файл).

B поле File Name (Имя файла) диалогового окна Save As (Сохранить как) введите c: \test\test.txt (если вы создали папку на томе C:).

5. Notepad выведет следующее сообщение об ошибке.

6. Это сообщение подразумевает, что каталог C: \Test не существует.

7. B трассировочной информации Filemon вы должны увидеть примерно то же, что и на следующей иллюстрации.

Вывод в нижней части иллюстрации был сгенерирован непосредственно перед появлением этого сообщения об ошибке. Строка 331 показывает, что Notepad пытался открыть C: \Test и получил отказ в доступе. Сразу после этого, как видно из строки 332, он попытался открыть каталог C: \Test\Test.txt (на это указывает флаг Directory в столбце Other на той же строке) и получил ошибку «файл не найден», потому что такого каталога нет. Сообщение Notepad «Path does not exist» (Путь не существует) согласуется с последней ошибкой, но не с первой. Поэтому кажется, что Notepad сначала пытался открыть каталог, а когда это не удалось, он почему-то решил, что имя C: \Test\Test.txt соответствует каталогу, а не файлу. He сумев открыть такой каталог, Notepad вывел сообщение об ошибке, но истинной причиной, как показывает Filemon, был отказ в доступе.

Filemon широко применяется Microsoft и другими организациями для решения трудных или почти неуловимых проблем. Рассмотрим один из примеров, где Filemon помог докопаться до истинной причины некорректного сообщения об ошибке, генерируемого службой Windows Installer. Когда пользователь пытался установить программу из ее файла Windows Installer Package, служба Windows Installer сообщала об ошибке, показанной на рис. 12–12; в нем утверждалось, что этой службе не удается записать что-либо в папку Temp. Пользователь убедился, что вопреки этому утверждению каталог, выделенный для временных файлов и заданный в его профиле (эту информацию он получил, набрав в консольном окне команду set temp), находится на томе, где достаточно свободного места, и имеет разрешения по умолчанию.

Рис. 12–12. Сообщение об ошибке от Microsoft Windows Installer

Тогда пользователь запустил Filemon дпя трассировки операций в файловой системе, которые приводят к этой ошибке, и обнаружил ее причину (см. выделенную строку на рис. 12–13). Из этих данных стало ясно, что служба Windows Installer обращалась не к каталогу временных файлов, заданному в профиле пользователя, а к \Windows\Installer. Строка со значением ACCESS DENIED указала на то, что служба Windows Installer выполнялась под учетной записью локальной системы, поэтому пользователь так изменил разрешения на доступ к \Windows\Installer, чтобы учетная запись локальной системы предоставляла права на доступ к этому каталогу дпя записи. Это и решило проблему.

Еще один пример анализа проблем с помощью Filemon. B этом случае пользователь запускал Microsoft Word, и буквально через несколько секунд набора текста окно Word закрывалось без всякого уведомления. Трассировочная информация Filemon, часть которой представлена на рис. 12–14, показала, что перед самым завершением Word повторно считывал одну и ту же часть файла с именем Mssp3es.lex. (Когда процесс завершается, система автоматически закрывает все открытые им описатели. Именно это и отражают строки с номерами от 25460.) Пользователь выяснил, что файлы с расширением. lex относятся к Microsoft Office Proofing Tools, и переустановил этот компонент, после чего проблема исчезла.

B третьем примере при каждом запуске Microsoft Excel выводилось сообщение об ошибке, показанное на рис. 12–15. Из трассировочной информации Filemon на рис. 12–16, полученной в ходе запуска Excel, обнаружилось, что Excel считывает файл 59403e20 из подкаталога Xlstart каталога, в который установлен Microsoft Office. Пользователь изучил документацию на Excel и выяснил, что Excel пытается автоматически открывать любые файлы, хранящиеся в каталоге Xlstart. Однако этот файл не имел никакого отношения к Excel, поэтому открыть его не удавалось и в итоге появлялось сообщение об ошибке. Удаление этого файла устранило проблему.

Рис. 12–15. Сообщение об ошибке при запуске Microsoft Excel

Последний пример связан с выявлением устаревших DLL. Пользователь запускал Microsoft Access 2000, и эта программа зависала, как только он пытался импортировать какой-нибудь файл Microsoft Excel. B другой системе с Microsoft Access 2000 тот же файл импортировался успешно. После трассировки операции импорта в обеих системах файлы журналов сравнивались с помощью Windiff. Результаты сравнения представлены на рис. 12–17.

Отбросив незначимые расхождения вроде разных имен временных файлов (строка 19) и разный регистр букв в именах файлов (строка 26), пользователь обнаружил первое существенное различие в результатах трассировки в строке 37. B системе, где импорт заканчивался неудачей, Microsoft Access загружал копию Accwiz.dll из каталога \Winnt\System32, тогда как в системе, где импорт проходил успешно, эта программа считывала Accwiz.dll из \Prog-ra~l\Files\Microsoft\Office. Пользователь изучил копию Accwiz.dll в \Winnt\ System32 и заметил, что она относится к более старой версии Microsoft Access, но порядок поиска DLL в системе приводил к тому, что этот экземпляр обнаруживался первым и до экземпляра нужной версии в каталоге, где установлен Microsoft Access 2000, дело не доходило. Удалив эту копию и зарегистрировав корректную версию, пользователь решил проблему.

Рис. 12–17. Сравнение журналов трассировки Microsoft Access в двух системах

Это лишь некоторые примеры, демонстрирующие, как с помощью Filemon выявлять истинные причины проблем в файловой системе, о которых приложения не всегда сообщают корректно. B остальной части главы мы сосредоточимся на описании «родной» для Windows файловой системы — NTFS.

Цели разработки и особенности NTFS

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

Требования к файловой системе класса «high end»

C самого начала разработка NTFS велась с учетом требований, предъявляемых к файловой системе корпоративного класса. Чтобы свести к минимуму потери данных в случае неожиданного выхода системы из строя или ее краха, файловая система должна гарантировать целостность своих метаданных. Дпя защиты конфиденциальных данных от несанкционированного доступа файловая система должна быть построена на интегрированной модели защиты. Наконец, она должна поддерживать защиту пользовательских данных за счет программной избыточности данных в качестве недорогой альтернативы аппаратным решениям. Здесь вы узнаете, как эти возможности реализованы в NTFS.

Восстанавливаемость

B соответствии с требованиями к надежности хранения данных и доступа к ним NTFS обеспечивает восстановление файловой системы на основе кон цепции атомарной транзакции (atomic transaction). Атомарные транзакции — это метод обработки изменений в базе данных, при котором сбои в работе системы не нарушают корректности или целостности базы данных. Суть атомарных транзакций заключается в том, что некоторые операции над базой данных, называемые транзакциями, выполняются по принципу «все или ничего». (Транзакцию можно определить как операцию ввода-вывода, изменяющую данные файловой системы или структуру каталогов тома.) Отдельные изменения на диске, составляющие транзакцию, выполняются атомарно: в ходе транзакции на диск должны быть внесены все требуемые изменения. Если транзакция прервана аварией системы, часть изменений, уже внесенных к этому моменту, нужно отменить. Такая отмена называется откатом (roll back). После отката база данных возвращается в исходное согласованное состояние, в котором она была до начала транзакции.

NTFS использует атомарные транзакции для реализации возможности восстановления файловой системы. Если некая программа инициирует операцию ввода-вывода, которая изменяет структуру NTFS-тома, т. е. модифицирует структуру каталогов, увеличивает длину файла, выделяет место под новый файл и др., то NTFS обрабатывает такую операцию как атомарную транзакцию. NTFS гарантирует, что транзакция будет либо полностью выполнена, либо отменена, если хотя бы одну из операций не удастся завершить из-за сбоя системы. O том, как это делается в NTFS, см. раздел «Поддержка восстановления в NTFS» далее в этой главе.

Кроме того, NTFS использует избыточность для хранения критически важной информации файловой системы, так что, если на диске появится сбойный сектор, она все равно сможет получить доступ к этой информации. Это одна из особенностей NTFS, отличающих ее от FAT и HPFS («родной» файловой системы OS/2).

Защита

Защита в NTFS построена на модели объектов Windows. Файлы и каталоги защищены от доступа пользователей, не имеющих соответствующих прав (подробнее о защите в Windows см. главу 8). Открытый файл реализуется в виде объекта «файл» с дескриптором защиты, хранящимся на диске как часть файла. Прежде чем процесс сможет открыть описатель какого-либо объекта, в том числе объекта «файл», система защиты Windows должна убедиться, что у этого процесса есть соответствующие полномочия. Дескриптор защиты в сочетании с требованием регистрации пользователя при входе в систему гарантирует, что ни один процесс не получит доступа к файлу без разрешения системного администратора или владельца файла.

Избыточность данных и отказоустойчивость

Восстанавливаемость NTFS действительно гарантирует, что файловая система тома останется доступной, но не дает гарантии полного восстановления пользовательских файлов. Последнее возможно за счет поддержки избыточности данных.

Избыточность данных для пользовательских файлов реализуется через многоуровневую модель драйверов Windows (см. главу 9), которая поддерживает отказоустойчивые диски. При записи данных на диск NTFS взаимодействует с диспетчером томов, а тот — с драйвером жесткого диска. Диспетчер томов может зеркалировать, или дублировать, данные одного диска на другом и таким образом позволяет при необходимости использовать данные с избыточной копии. Поддержка таких функций обычно называется RAID уровня 1. Диспетчеры томов также могут записывать данные в чередующиеся области (stripes) на три и более дисков, используя один диск для хранения информации о четности. Если данные на одном диске потеряны или стали недоступными, драйвер может реконструировать содержимое диска с помощью логической операции XOR. Такая поддержка называется RAID уровня 5 (подробнее о чередующихся и зеркальных томах, а также о томах RAID-5 см. главу 10).

Дополнительные возможности NTFS

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

• множественные потоки данных;

• имена на основе Unicode;

• универсальный механизм индексации;

• динамическое переназначение плохих кластеров;

• жесткие связи и точки соединения;

• сжатие и разреженные файлы;

• протоколирование изменений;

• квоты томов, индивидуальные для каждого пользователя;

• отслеживание ссылок;

• шифрование;

• поддержка POSIX;

• дефрагментация

• поддержка доступа только для чтения.

Обзор этих возможностей приводится в следующих разделах.

Множественные потоки данных

B NTFS каждая единица информации, сопоставленная с файлом (имя и владелец файла, метка времени и т. д.), реализована в виде атрибута файла (атрибута объекта NTFS). Каждый атрибут состоит из одного потока данных (stream), т. е. из простой последовательности байтов. Это позволяет легко добавлять к файлу новые атрибуты (и соответственно новые потоки). Поскольку данные являются всего лишь одним из атрибутов файла и поскольку можно добавлять новые атрибуты, файлы и каталоги NTFS могут содержать несколько потоков данных.

B любом файле NTFS по умолчанию имеется один безымянный поток данных. Приложения могут создавать дополнительные, именованные потоки данных и обращаться к ним по именам. Чтобы не изменять Windows-функции API ввода-вывода, которым имя файла передается как строковый аргумент, имена потоков данных задаются через двоеточие (:) после имени файла, например:

myfile.dat: stream2

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

Множественные потоки данных использует, например, компонент поддержки файлового сервера Apple Macintosh, поставляемый с Windows Server. Системы Macintosh используют два потока данных в каждом файле: один — для хранения данных, другой — для хранения информации о ресурсах (тип файла, значок, представляющий файл в пользовательском интерфейсе, и т. д.). Поскольку NTFS поддерживает множественные потоки данных, пользователь Macintosh может скопировать целую папку Macintosh на сервер Windows, а другой пользователь Macintosh может скопировать ее с сервера без потери информации о ресурсах.

Windows Explorer — еще одно приложение, использующее такие потоки. Когда вы щелкаете правой кнопкой мыши файл NTFS и выбираете команду Properties, на экране появляется диалоговое окно, вкладка Summary (Сводка) которого позволяет сопоставить с файлом такую информацию, как заголовок, тема, имя автора и ключевые слова. Windows Explorer хранит эту информацию в альтернативном потоке под названием Summary Information, добавляемом к файлу.

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

ЭКСПЕРИМЕНТ: просмотр потоков

Большинство приложений Windows не рассчитано на работу с дополнительными именованными потоками, но команды echo и more поддерживают эту функциональность. Таким образом, самый простой способ понаблюдать за потоками данных в действии — создать именованный поток с помощью echo, а затем вывести его на экран с помощью more. B результате выполнения команд, показанных ниже, создается файл test с потоком stream.

При перечислении содержимого каталога размер файла test не отражает данные, хранящиеся в дополнительном потоке, поскольку в этом случае NTFS возвращает размер только безымянного потока данных.

Утилита Streams (wwwsysinternals.com) позволяет определить, в каких файлах и каталогах есть дополнительные потоки данных:

Имена на основе Unicode

Как и Windows в целом, NTFS полностью поддерживает Unicode, используя Unicode-символы для хранения имен файлов, каталогов и томов. Unicode, 16-битная кодировка символов, обеспечивает уникальное представление любого символа основных языков мира, что упрощает обмен информацией между странами. Поскольку в Unicode имеется уникальное представление каждого символа, последний не зависит от того, какая кодовая страница загружена в операционную систему. Длина имени каждого каталога или файла в пути может достигать 255 символов; в нем могут быть символы Unicode, пробелы и несколько точек.

Универсальный механизм индексации

Архитектура NTFS позволяет индексировать атрибуты файлов на дисковом томе. Это дает возможность файловой системе вести эффективный поиск файлов по неким критериям, например находить все файлы в определенном каталоге. Файловая система FAT индексирует имена файлов, но не сортирует их, что замедляет просмотр больших каталогов.

Некоторые функции NTFS используют преимущества универсальной индексации, в том числе консолидированных дескрипторов защиты, где дескрипторы защиты файлов и каталогов на томе хранятся в едином внутреннем потоке без дубликатов; при этом они индексируются с использованием внутреннего идентификатора защиты, определяемого NTFS. Об индексации с помощью этих средств см. раздел «Структура NTFS на диске» далее в этой главе.

Динамическое переназначение плохих кластеров

Обычно, если программа пытается считать данные из плохого сектора диска, операция чтения заканчивается неудачей, а данные соответствующего кластера становятся недоступными. Однако, если диск отформатирован как отказоустойчивый том NTFS, специальный драйвер Windows динамически считывает «хорошую» копию данных, хранившихся в плохом секторе, и посылает NTFS предупреждение о плохом секторе. NTFS выделяет новый кластер, заменяющий тот, в котором находится плохой сектор, и копирует данные в этот кластер. Плохой кластер помечается как аварийный и больше не используется. Восстановление данных и динамическое переназначение плохих кластеров особенно полезно для файловых серверов и отказоустойчивых систем, а также для всех приложений, в которых потеря данных недопустима. Если на момент появления плохого сектора диспетчер томов не был загружен, NTFS все равно заменяет кластер и не допускает его повторного использования, хотя восстановить данные из плохого сектора уже не удастся.

Жесткие связи и точки соединения

Жесткие связи (hard links) позволяют ссылаться на один и тот же файл по нескольким путям (для каталогов жесткие связи не поддерживаются). Если вы создаете жесткую связь с именем C: \Users\Documents\Spec.doc, которая ссылается на существующий файл C: \My Documents\Spec.doc, то с одним дисковым файлом будут связаны два пути, и вы сможете обращаться к этому файлу, используя оба пути. Процессы могут создавать жесткие связи вызовом Windows-функции CreateHardLink или POSIX-функции ln.

B дополнение к жестким связям NTFS поддерживает другой тип перенаправления — точки соединения (junctions), также называемые символьными ссылками (symbolic links). Они позволяют перенаправлять трансляцию имени файла или каталога из одного каталога в другой. Например, если путь C: \Drivers — ссылка, которая перенаправляет в C: \Windows\System32\Drivers, то приложение, читающее C: \Drivers\Ntfs.sys, на самом деле читает C: \Windows\System\Drivers\Ntfs.sys. Точки соединения представляют собой удобное средство «подъема» каталогов, расположенных слишком глубоко в дереве каталогов, на более высокий уровень, не нарушая исходной структуры или содержимого дерева каталогов. Так, в предыдущем примере каталог драйверов «поднят» на два уровня по сравнению с реальным. Точки соединения неприменимы к удаленным каталогам — они используются только для каталогов на локальных томах.

Точки соединения опираются на механизм NTFS «точки повторного разбора» (см. раздел «Точки повторного разбора» далее в этой главе). Точка повторного разбора (reparse point) — это файл или каталог, с которым сопоставлен блок данных, называемых данными повторного разбора (reparse data); они представляют собой пользовательские данные о файле или каталоге, например о его состоянии или местонахождении. Эти данные могут быть считаны из точки повторного разбора приложением, которое создало их, драйвером файловой системы или диспетчером ввода-вывода. Обнаружив точку повторного разбора при поиске файла или каталога, NTFS возвращает код статуса повторного разбора, который сигнализирует драйверам фильтров файловой системы, подключенным к дисковому тому, и диспетчеру ввода-вывода о необходимости анализа данных повторного разбора. Каждый тип точек повторного разбора имеет уникальный тэг повторного разбора (reparse tag) — он позволяет компоненту, отвечающему за интерпретацию данных конкретной точки повторного разбора, распознавать свои точки разбора, не проверяя их данные. Далее владелец тэга повторного разбора (драйвер фильтра файловой системы или диспетчер ввода-вывода) может выбрать один из следующих вариантов дальнейших действий.

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

• Владелец тэга повторного разбора может удалить из файла точку повторного разбора, каким-либо образом изменить файл, а затем инициировать новую операцию файлового ввода-вывода. По такому принципу точки повторного разбора используются системой Hierarchical Storage Management (HSM). HSM архивирует файлы, перемещая их содержимое на ленточные накопители и оставляя вместо содержимого файлов точки повторного разбора. Когда какой-либо процесс обращается к архивированному файлу, драйвер фильтра HSM (\Windows\System32\Drivers\Rsfilter.sys) удаляет из этого файла точку повторного разбора, считывает его данные с архивного носителя и инициирует повторное обращение к файлу. Windows-функций для создания точек повторного разбора нет. Вместо них процессы должны использовать управляющий код FSCTL_SET_REPARSE_ POINT файловой системы в сочетании с Windows-функцией DeviceIoControl.

Процесс может запросить содержимое точки повторного разбора с помощью управляющего кода FSCTL_GET_REPARSE_POINT. B атрибутах файла, сопоставленного с точкой повторного разбора, присутствует флаг FILEAT-TRIBUTE_REPARSE_POINT, что позволяет приложениям проверять наличие точек повторного разбора вызовом Windows-функции GetFileAttributes.

ЭКСПЕРИМЕНТ: создание точки соединения

B Windows нет средств для создания точек соединения, но вы можете создать такую точку с помощью утилиты Junction (wwwsysinternak.com) или Linkd из ресурсов Windows. Утилита Linkd позволяет просмотреть определения существующих точек соединения, a Junction — вывести информацию о точках соединения и точках повторного разбора.

Сжатие и разреженные файлы

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

Приложения сжимают и разархивируют файлы, передавая DeviceIoControl управляющий код FSCTL_SET_COMPRESSION. Для запроса состояния сжатия файла или каталога используется управляющий код FSCTL_GET_COMPRES-SION. У сжатого файла или каталога установлен флаг FILE_ATTRIBUTE_COM-PRESSED, поэтому приложения могут определять состояние сжатия файла или каталога вызовом GetFileAttributes.

Второй тип сжатия известен под названием разреженные файлы (sparse files). Если файл помечен как разреженный, NTFS не выделяет на томе место для тех частей файла, которые определены приложением как пустые. При чтении приложением пустых областей разреженного файла NTFS просто возвращает буферы, заполненные нулевыми значениями. Этот тип сжатия полезен для клиент-серверных приложений, в которых реализовано протоколирование с циклическими буферами (circular-buffer logging): сервер регистрирует информацию в файле, а клиент асинхронно считывает ее. Поскольку информация, уже считанная клиентом, больше не нужна, продолжать хранить ее в файле не требуется. Если такой файл является разреженным, клиент может определять считанные им области как пустые, тем самым освобождая место на томе. A сервер может добавлять новую информацию в файл, не опасаясь, что он в конечном счете займет все свободное пространство на томе.

Как и в случае сжатых файлов, NTFS прозрачно управляет разреженными файлами. Приложения указывают состояние разреженности файла, передавая DeviceIoControl управляющий код FSCTL_SET_SPARSE. Чтобы определить диапазон файла как пустой, приложения используют код FSCTL_SET_ 2ERO_DATA, а чтобы запросить у NTFS описание того, какие части файла являются разреженными, — код FSCTL_QUERY_ALLOCATED_RANGES. Разреженные файлы применяются, в частности, в журнале изменений NTFS, о котором мы расскажем в следующем разделе.

Протоколирование изменений

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

Альтернативный подход для приложения заключается в том, чтобы зарегистрироваться на получение уведомлений об изменении содержимого каталогов. Для этого предназначена Windows-функция FindFirstChangeNotifica-tion или ReadDirectoryChangesW. B качестве входного параметра приложение указывает имя нужного каталога, и функция сообщает о любом изменении в содержимом этого каталога. Хотя этот подход более эффективен, чем сканирование тома, он требует непрерывной работы приложения. При этом приложениям все равно может понадобиться сканирование каталогов, так как FindFirstChangeNotification сообщает лишь о факте изменений, а не о конкретных изменениях. B то же время ReadDirectoryChangesW принимает от приложения буфер, который FSD заполняет записями об изменениях. Ho при переполнении буфера приложение должно быть готово вернуться к сканированию каталога.

NTFS предусматривает третий подход, в котором преодолены недостатки первых двух: приложение может настроить журнал изменений NTFS с помощью функции DeviceIoControl и управляющего кода FSCTL_CREATE_USNJOURNAL; тогда NTFS будет регистрировать информацию об изменениях файлов и каталогов во внутреннем файле — журнале изменений (change journal). Этот журнал обычно достаточно велик, что дает приложению шанс обработать все без исключения изменения. Для чтения записей в журнале изменений предназначен управляющий код FSCTL_QUERY_USNJOURNAL; при этом можно указать, чтобы функция DeviceIoControl не завершалась до тех пор, пока в журнале не появятся новые записи.

Квоты томов, индивидуальные для каждого пользователя

Системным администраторам часто бывает нужно отслеживать или ограничивать дисковое пространство, занимаемое пользователями на общих томах в сети. Поэтому NTFS поддерживает управление дисковым пространством на основе квот, позволяя выделять квоты каждому пользователю. NTFS можно настроить на запись в системный журнал события, возникающего в тот момент, когда пользователь превышает пороговое значение, близкое к лимиту. При попытке пользователя занять больше места, чем разрешает его квота, NTFS также регистрирует соответствующее событие в системном журнале и, кроме того, завершает файловый ввод-вывод приложения, вызвавшего нарушение квоты, с кодом ошибки «disk full» («диск заполнен»).

NTFS отслеживает использование тома благодаря тому факту, что помечает файлы и каталоги идентификаторами защиты (SID) пользователей, создавших эти объекты на томе (определение SID см. в главе 8). Сумма логических размеров файлов и каталогов, принадлежащих пользователю, сравнивается с квотой, определенной администратором. Поэтому пользователь не может превысить свою квоту, создав пустой разреженный файл и потом заполняя его ненулевыми значениями. Кстати, хотя 50-килобайтный файл может быть сжат до 10 Кб, при учете используется его исходный размер — 50 Кб.

По умолчанию отслеживание квот на томах отключено. Чтобы разрешить отслеживание квот, указать пороговые значения для выдачи предупреждений, задать ограничения и настроить реакцию NTFS на достижение одного из этих пороговых значений, используйте вкладку Quota (Квота) окна свойств тома (рис. 12–18). Диалоговое окно Quota Entries (Записи квот), которое можно открыть с этой вкладки, позволяет администратору задавать различные лимиты и поведение NTFS для каждого пользователя. Приложения, которым требуется управление на основе квот NTFS, используют СОМ-интерфейсы квот, в том числе IDiskQuotaControl, IDiskQuotaUser и IDiskQuotaEvents.

Отслеживание ссылок

B пространстве имен оболочки Windows (например, на рабочем столе) можно создавать ярлыки (shortcuts) файлов, находящихся в пространстве имен файловой системы. Такие ярлыки используются, например, в меню Start.

Аналогичным образом OLE-связи позволяют встраивать документы одних приложений в документы, созданные другими приложениями. OLE-связи поддерживаются всеми приложениями из пакета Microsoft Office, включая PowerPoint, Excel и Word.

Хотя OLE-связи дают возможность легко соединять файлы друг с другом и с пространством имен оболочки, управлять ими в прошлом было нелегко. Если пользователь Windows NT 4, Windows 95 или Windows 98 перемещал источник OLE-связи или ярлыка оболочки (источником называется файл или каталог, на который ссылается OLE-связь или ярлык), связь разрывалась, и система предпринимала попытку найти источник связи эвристическим методом. B Windows файловая система NTFS включает поддержку отслеживания распределенных связей (distributed link-tracking), обеспечивающую целостность ярлыков оболочки и OLE-связей при перемещении источников на другой том NTFS в пределах одного домена.

Отслеживание связей в NTFS реализуется на основе необязательного атрибута файла, известного под названием идентификатор объекта (object ID). Приложение может назначить такой идентификатор файлу с помощью управляющих кодов файловой системы FSCTL_CREATE_OR_GET_OBJECT_ID (назначает идентификатор, если он еще не назначен) и FSCTL_SET_OBJECT_ID. Идентификаторы объектов можно запросить с помощью управляющих кодов FSCTL_CREATE_OR_GET_OBJECT_ID и FSCTL_GET_OBJECT_ID. Код FSCTL_DELETE_OBJECT_ID позволяет удалять идентификаторы объектов из файлов.

Шифрование

Корпоративные пользователи часто хранят на своих компьютерах конфиденциальную информацию. Хотя данные на серверах компаний обычно надежно защищены, информация, хранящаяся на портативном компьютере, может попасть в чужие руки в случае потери или кражи компьютера. Права доступа к файлам NTFS в таком случае не защитят данные, поскольку полный доступ к томам NTFS можно получить независимо от их защиты — достаточно воспользоваться программами, умеющими читать файлы NTFS вне среды Windows. Более того, права доступа к файлам NTFS становятся бесполезны при использовании другой системы Windows и учетной записи администратора. Вспомните из главы 8, что учетная запись администратора обладает привилегиями захвата во владение и резервного копирования, любая из которых позволяет получить доступ к любому защищенному объекту в обход его параметров защиты.

NTFS поддерживает механизм Encrypting File System (EFS), с помощью которого пользователи могут шифровать конфиденциальные данные. EFS, как и механизм сжатия файлов, полностью прозрачен для приложений. Это означает, что данные автоматически расшифровываются при чтении их приложением, работающим под учетной записью пользователя, который имеет права на просмотр этих данных, и автоматически шифруются при изменении их авторизованным приложением.

ПРИМЕЧАНИЕ NTFS не допускает шифрования файлов, расположенных в корневом каталоге системного тома или в каталоге \Windows, поскольку многие находящиеся там файлы нужны в процессе загрузки, когда EFS еще не активна.

EFS использует криптографические сервисы, предоставляемые Windows в пользовательском режиме, и состоит из драйвера устройства режима ядра, тесно интегрированного с NTFS и DLL-модулями пользовательского режима, которые взаимодействуют с подсистемой локальной аутентификации (LSASS) и криптографическими DLL.

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

Windows-функции EncryptFile и DecryptFile позволяют шифровать и дешифровать файлы, a FileEncryptionStatus — получать атрибуты файла или каталога, связанного с EFS, — например, чтобы определить, зашифрован ли данный файл или каталог.

Поддержка POSIX

Как говорилось в главе 2, одно из требований KWindows состояло в том, что она должна полностью поддерживать стандарт POSIX 1003.1. Стандарт POSIX требует от файловой системы поддержки имен файлов и каталогов, чувствительных к регистру букв, цепочечных разрешений (traversal permissions) (для доступа к файлу нужны права на доступ к каждому каталогу на пути к этому файлу), метки времени изменения файла (отличной от метки времени последней модификации файла в MS-DOS) и жестких связей. Вся эта функциональность в NTFS реализована.

Дефрагментация

Многие верят в то, что NTFS якобы автоматически оптимизирует размещение файлов на диске, не допуская их фрагментации. Хотя она стремится записывать файлы в непрерывные области, со временем файлы на томе могут стать фрагментированными — особенно когда свободного пространства мало. Файл является фрагментированным, если его данные занимают несмежные кластеры. Так, на рис. 12–19 показан фрагментированный файл, состоящий из трех фрагментов. Ho, как и большинство файловых систем (включая версию FAT в Windows), NTFS не предпринимает специальных мер по поддержанию непрерывного размещения файлов на диске — разве что резервирует область дискового пространства, называемую зоной главной таблицы файлов (master file table, MFT), где и хранится MFT (NTFS разрешает выделять место под файлы из зоны MFT, когда на томе остается мало свободного пространства.) Подробнее о MFT см. раздел «Главная таблица файлов» далее в этой главе.

Фрагментированный файл Непрерывный файл

Для упрощения разработки независимых средств дефрагментации дисков в Windows включен API дефрагментации, с помощью которого подобные утилиты могут перемещать файловые данные так, чтобы файлы занимали непрерывные кластеры. Этот API реализован на основе управляющих кодов файловой системы: FSCTL_GET_VOLUME_BITMAP (возвращает карту свободных и занятых кластеров тома), FSCTL_GET_RETRIEVAL_POINTERS (возвращает карту кластеров, занятых файлом) и FSCTL_MOVE_FILE (перемещает файл).

B Windows имеется встроенная программа дефрагментации, доступная через утилиту Disk Defragmenter (\Windows\System32\Dfrg.msc). Ей присущ ряд ограничений, в частности в Windows 2000 эту утилиту нельзя запускать из командной строки или автоматически запускать по заданному расписанию. B Windows XP и Windows Server 2003 у программы дефрагментации появился интерфейс командной строки, \Windows\System32\Defrag.exe, позволяющий запускать процесс дефрагментации интерактивно или по расписанию, но она по-прежнему не сообщает детальных отчетов и не поддерживает такие возможности, как исключение определенных файлов или каталогов из процесса дефрагментации. Сторонние утилиты дефрагментации дисков, как правило, предлагают более богатую функциональность.

B Windows XP поддержка дефрагментации для NTFS была переписана, чтобы снять часть ограничений, которые были в Windows 2000. Так, в Windows 2000 поддержка дефрагментации NTFS опиралась на диспетчер кэша, что создавало ряд ограничений, например невозможность перемещения файловых блоков размером более 256 Кб или дефрагментации файлов метаданных NTFS. (Заметьте, что 256 Кб — это размер представлений, поддерживаемых диспетчером кэша. Подробнее о диспетчере кэша см. главу 11.) B реализации дефрагментации NTFS в Windows XP и Windows Server 2003 существует лишь одно ограничение — нельзя дефрагментировать страничные файлы и файлы журналов NTFS.

Поддержка доступа только для чтения

До Windows XP драйвер файловой системы NTFS поддерживал монтирование тома исключительно на записываемом носителе, куда он должен был помещать файлы журналов транзакций. Драйверы NTFS в Windows XP и Windows Server 2003 могут монтировать тома на носителях только для чтения; эта функциональность нужна во встраиваемых системах, в которых базовые образы файловой системы (base file system images) доступны только для чтения.

Драйвер файловой системы NTFS

Как отмечалось в главе 9, подсистема ввода-вывода Windows устроена так, что NTFS и другие файловые системы представляют собой загружаемые драйверы устройств режима ядра. Они неявно вызываются приложениями, использующими Windows или другие API ввода-вывода (например, POSIX). Как показано на рис. 12–20, подсистемы окружения вызывают системные сервисы, которые в свою очередь находят соответствующие загруженные драйверы и вызывают их. (O диспетчеризации системных сервисов см. раздел «Диспетчеризация системных сервисов» главы 3.)

Драйверы передают друг другу запросы ввода-вывода, вызывая диспетчер ввода-вывода исполнительной системы. Использование диспетчера ввода-вывода в качестве промежуточного звена обеспечивает независимость каждого драйвера, что позволяет загружать и выгружать его без последствий для других драйверов. Кроме того, драйвер NTFS взаимодействует с тремя другими компонентами исполнительной системы (рис. 12–21), тесно связанными с файловыми системами.

Сервис файла журнала (log file service, LFS) является частью NTFS и предоставляет функции для поддержки журнала изменений на диске. Файл журнала LFS используется при восстановлении тома NTFS в случае аварии системы (подробнее о LFS см. раздел «Сервис файла журнала» далее в этой главе).

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

NTFS участвует в модели объектов Windows, реализуя файлы в виде объектов. Такая реализация допускает совместное использование файлов и их защиту диспетчером объектов, который управляет всеми объектами уровня исполнительной системы (о диспетчере объектов см. главу 3).

Приложение создает файлы и обращается к ним так же, как и к любым другим объектам Windows, — через описатели объектов. K тому времени, когда запрос ввода-вывода достигает NTFS, диспетчер объектов и система защиты уже убедились в наличии у вызывающего процесса прав на запрошенные им виды доступа к объекту «файл». Система защиты сравнила маркер доступа вызывающего процесса с элементами ACL этого объекта «файл» (подробнее об ACL см. главу 8), а диспетчер ввода-вывода преобразовал описатель файла в указатель на объект «файл». NTFS использует информацию из объекта «файл» для обращения к файлу на диске.

Ha рис. 12–22 показаны структуры данных, связывающие описатель файла со структурой файловой системы на диске.

NTFS получает адрес файла на диске из объекта «файл» по нескольким указателям. Как видно из рис. 12–22, объект «файл», представляющий один вызов системного сервиса для открытия файла, указывает на блок управления потоком данных (stream control block, SCB) для атрибута, который вызывающая программа пытается считать или записать. B нашем случае процесс открыл как безымянный атрибут данных, так и именованный поток (альтернативный атрибут данных) файла. SCB представляют отдельные атрибуты файла и содержат информацию о том, как искать конкретные атрибуты внутри файла. Все SCB файла указывают на общую структуру данных — блок управления файлом (file control block, FCB). FCB содержит указатель на запись файла в главной таблице файлов (MFT), о которой мы поговорим в следующем разделе.

Структура NTFS на диске

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

Тома

Структура NTFS начинается с тома. Том (volume) соответствует логическому разделу на диске и создается при форматировании диска или его части под NTFS. Оснастка Disk Management (Управление дисками) консоли MMC также позволяет создать том RAID, охватывающий несколько дисков.

Ha диске может быть один или несколько томов. NTFS обрабатывает каждый том независимо от других. Три примера конфигурации 150-мегабайтного жесткого диска показаны на рис. 12–23.

Том состоит из набора файлов и свободного пространства, оставшегося в данном разделе диска. B FAT том также содержит области, специально отформатированные для использования файловой системой. Ho в томе NTFS все данные файловой системы вроде битовых карт, каталогов и даже начального загрузочного кода хранятся как обычные файлы.

ПРИМЕЧАНИЕ У NTFS-TOMOB в Windows 2000 дисковый формат версии 3.0; в Windows XP и Windows Server 2003 в этот формат были внесены незначительные изменения, и теперь используется его версия 3.1. Номер версии тома хранится в файле метаданных IVolume.

Кластеры

Размер кластера на томе NTFS, или кластерный множитель (cluster factor), устанавливается при форматировании тома командой format или в оснастке Disk Management (Управление дисками). Размер кластера по умолчанию определяется размером тома, но всегда содержит целое число физических секторов с дискретностью N2 (т. е. 1 сектор, 2 сектора, 4 сектора, 8 секторов и т. д.). Кластерный множитель выражается числом байтов в кластере, например 512 байт, 1 Кб или 2 Кб.

Внутренне NTFS работает только с кластерами. (Однако NTFS инициирует низкоуровневые операции ввода-вывода на томе, выравнивая передаваемые данные по размеру сектора и подгоняя их объем под значение, кратное размеру секторов.) NTFS использует кластер как единицу выделения пространства для поддержания независимости от размера физического сектора. Это позволяет NTFS эффективно работать с очень большими дисками, используя кластеры большего размера, и поддерживать нестандартные диски с размером секторов, отличным от 512 байтов. Применение больших кластеров на больших томах уменьшает фрагментацию и ускоряет выделение свободного пространства за счет небольшого проигрыша в эффективности использования дискового пространства. Команда format или оснастка Disk Management выбирает кластерный множитель в зависимости от размера тома, но это значение можно изменить.

NTFS адресуется к конкретным местам на диске, используя логические номера кластеров (logical cluster numbers, LCN). Для этого все кластеры на томе просто нумеруются по порядку — от начала до конца. Для преобразования LCN в физический адрес на диске NTFS умножает LCN на кластерный множитель и получает байтовое смещение от начала тома, воспринимаемое интерфейсом драйвера диска. Ha данные внутри файла NTFS ссылается по виртуальным номерам кластеров (virtual cluster numbers, VCN), нумеруя кластеры, которые принадлежат конкретному файлу (от 0 до m). VCN не обязательно должны быть физически непрерывными.

Главная таблица файлов

B NTFS все данные, хранящиеся на томе, содержатся в файлах. Это относится и к структурам данных, используемым для поиска и выборки файлов, к начальному загрузочному коду и битовой карте, в которой регистрируется состояние пространства всего тома (метаданные NTFS). Хранение всех видов данных в файлах позволяет файловой системе легко находить и поддерживать данные, а каждый файл может быть защищен дескриптором защиты. Кроме того, при появлении плохих секторов на диске, NTFS может переместить файлы метаданных.

Главная таблица файлов (MFT) занимает центральное место в структуре NTFS-тома. MFT реализована как массив записей о файлах. Размер каждой записи фиксирован и равен 1 Кб (см. раздел «Записи о файлах» далее в этой главе). Логически MFT содержит по одной строке на каждый файл тома, включая строку для самой MFT Кроме MFT, на каждом томе NTFS имеется набор файлов метаданных с информацией, необходимой для реализации структуры файловой системы. Имена всех файлов метаданных NTFS начинаются со знака доллара ($), хотя эти знаки скрыты. Так, имя файла MFT — $Mft. Остальные файлы NTFS-тома являются обычными файлами и каталогами (рис. 12–24).

Обычно каждая запись MFT соответствует отдельному файлу Ho если у файла много атрибутов или он сильно фрагментирован, для него может понадобиться более одной записи. Тогда первая запись MFT, хранящая адреса других записей, называется базовой (base file record).

ЭКСПЕРИМЕНТ: просмотр MFT

Утилита Nfi из OEM Support Tools (входит в отладочные средства Windows; ее можно скачать с support.mlcrosoft.com/support/eb/articIes/Q253/ 0/66.asp) позволяет получить дамп содержимого MFT тома NTFS и преобразовать номер кластера тома или номер сектора физического диска (не для RAID-томов) в имя соответствующего файла (если кластер или сектор занят файлом). Первые 16 элементов MFT зарезервированы для файлов метаданных, но записи для файлов необязательных метаданных (которые присутствуют только при использовании на томе соответствующих возможностей) находятся вне этой области: \$Extend\$Quota, \$Extend\$ObjId, \$Extend\$UsnJrnl и \$Extend\$Reparse. Следующий дамп получен для тома, на котором используются точки повторного разбора ($Reparse), квоты ($Quota) и идентификаторы объектов ($ObjId).

При первом обращении к тому NTFS должна смонтировать его, т. е. считать с диска метаданные и сформировать внутренние структуры данных, необходимые для обработки обращений к файловой системе. Чтобы смонтировать том, NTFS ищет в загрузочном секторе физический адрес MFT на диске. Запись о самой MFT является первым элементом в этой таблице, вторая запись указывает на файл в середине диска ($MftMirr), который называется зеркальной копией MFT и содержит копию первых нескольких записей MFT Если по каким-либо причинам считать часть MFT не удастся, для поиска файлов метаданных будет использована именно эта копия MFT.

Найдя запись для MFT, NTFS получает из ее атрибута данных информацию о сопоставлении VCN и LCN и сохраняет ее в памяти. B каждой группе (run) хранится сопоставление VCN-LCN и длина этой группы — вот и вся информация, необходимая для того, чтобы найти LCN по VCN. Эта информация сообщает NTFS, где на диске искать группы, образующие MFT (O группах см. раздел «Резидентные и нерезидентные атрибуты» далее в этой главе.) Затем NTFS обрабатывает записи MFT еще для нескольких файлов метаданных и открывает эти файлы. Наконец, NTFS выполняет операцию восстановления фаршовой системы (см. раздел «Восстановление» далее в этой главе) и открывает остальные файлы метаданных. C этого момента пользователь может обращаться к данному дисковому тому.

ПРИМЕЧАНИЕ Для упрощения восприятия материала в тексте и на схемах в этой главе группа обозначается как сущность, содержащая VCN, LCN и длину группы. Ho на самом деле NTFS сжимает эту информацию на диске в пары «LCN — следующий VCN». Зная начальный VCN, NTFS может определить длину группы простым вычитанием начального VCN из следующего VCN.

B процессе работы системы NTFS ведет запись в другой важный файл метаданных — файл журнала с именем $LogFile. NTFS использует его для регистрации всех операций, влияющих на структуру тома NTFS, в том числе для регистрации создания файлов и выполнения любых команд вроде Сору, модифицирующих структуру каталогов. Файл журнала используется и при восстановлении тома NTFS после аварии системы.

Еще один элемент MFT зарезервирован для корневого каталога (также обозначаемого как «\»). Его запись содержит индекс файлов и каталогов, хранящихся в корне структуры каталогов NTFS. Когда NTFS впервые получает запрос на открытие файла, она начинает его поиск с записи корневого каталога. Открыв файл, NTFS сохраняет файловую ссылку MFT для этого файла и поэтому в следующий раз, когда понадобится считать или записать тот же файл, сможет напрямую обратиться к его записи в MFT.

NTFS регистрирует распределение дискового пространства в файле битовой карты (bitmap file) с именем $Bitmap. Атрибут данных для файла битовой карты содержит битовую карту, каждый бит которой представляет кластер тома и сообщает, свободен кластер или выделен.

Файл защиты (security file) с именем $Secure хранит базу данных дескрипторов защиты, действующих в пределах тома. Дескрипторы защиты файлов и каталогов NTFS можно настраивать индивидуально, но для экономии места NTFS хранит настройки дескрипторов защиты в общем файле, который позволяет файлам и каталогам с одинаковыми параметрами защиты ссылаться на один и тот же дескриптор защиты. Такая оптимизация дает существенную экономию в большинстве сред, потому что в них целые деревья каталогов имеют одинаковые параметры защиты.

Другой системный файл, загрузочный (boot file), с именем $Boot хранит код начальной загрузки Windows. Для успешного запуска системы код начальной загрузки должен находиться на диске в определенном месте. При форматировании команда format определяет это место в виде файла, создавая для него запись в MFT. При этом NTFS следует своим правилам, согласно которым все данные хранятся на диске в виде файлов. Загрузочный файл, как и файлы метаданных NTFS, может быть защищен индивидуальным дескриптором защиты. B такой модели «на диске есть только файлы» код начальной загрузки может быть изменен путем обычного файлового ввода-вывода, хотя загрузочный файл защищен от редактирования.

NTFS поддерживает файл плохих кластеров (bad-cluster file) с именем $BadClus, в котором регистрируются все сбойные участки дискового тома, и файл тома (volume file) с именем $Volume, который содержит имя тома, версию NTFS, под которую отформатирован том, и бит, устанавливаемый при каком-либо повреждении диска. Если этот бит установлен, диск должен быть восстановлен утилитой Chkdsk. Файл сопоставления имен с буквами в верхнем регистре (uppercase file) с именем $UpCase включает таблицу трансляции букв между верхним и нижним регистрами. NTFS также поддерживает файл, содержащий таблицу определения атрибутов (attribute definition table), с именем IAttrDef; в нем определяются типы атрибутов, поддерживаемые томом, и указывается, являются ли они индексируемыми, следует ли их восстанавливать в ходе операции восстановления системы и т. д.

Некоторые файлы метаданных NTFS хранит в каталоге расширенных метаданных $Extend, в том числе помещая туда файл идентификаторов объектов ($ObjId), файл квот ($Quota), файл журнала регистрации изменений ($UsnJrnl) и файл точек повторного разбора ($Reparse). B этих файлах содержится информация, относящаяся к дополнительным возможностям NTFS. Файл идентификаторов объектов хранит идентификаторы объектов «файл», файл квот — значения квот и информацию о поведении томов, на которых используются квоты, файл точек повторного разбора — список файлов и каталогов, включающих данные точек повторного разбора, а в файле журнала изменений регистрируются изменения файлов и каталогов.

ЭКСПЕРИМЕНТ: просмотр информации NTFS

Для просмотра информации о NTFS-томе, в том числе о размещении и размере MFT и зоны MFT, вы можете использовать в Windows 2000 утилиту NTFSInfo (с сайта wwwsysinternals.com), а в Windows XP или Windows Server 2003 — встроенную программу командной строки Fsutil.exe:

Структура файловых ссылок

Файл на томе NTFS идентифицируется 64-битным значением, которое называется файловой ссылкой (file reference). Файловая ссылка состоит из номера файла и номера последовательности. Номер файла равен позиции его записи в MFT минус 1 (или позиции базовой записи в MFT минус 1, если файл требует несколько записей). Номер последовательности в файловой ссылке увеличивается на 1 при каждом повторном использовании позиции записи в MFT, что позволяет NTFS проверять внутреннюю целостность файловой системы. Файловую ссылку иллюстрирует рис. 12–25.

Записи о файлах

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

метку времени и, возможно, дополнительные именованные атрибуты данных. Запись МFТ для небольшого файла показана на рис. 12–26.

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

B таблице 12-4 перечислены атрибуты для файлов на томах NTFS (не у каждого файла есть все эти атрибуты).

B таблице 12-4 даны имена атрибутов, но на самом деле атрибуты соответствуют числовым кодам типов, используемым NTFS для упорядочения атрибутов в записи о файле. Файловые атрибуты в записи MFT размещаются в порядке возрастания числовых значений этих кодов. Некоторые типы атрибутов встречаются в записи дважды — например, если у файла несколько атрибутов данных или несколько имен.

Каждый атрибут в записи о файле идентифицируется кодом типа атрибута, имеет значение и необязательное имя. Значение атрибута представляет собой байтовый поток. Так, значением атрибута $FILENAME является имя файла, значением атрибута $DATA — произвольный набор байтов, сохраненный пользователем в файле.

У большинства атрибутов нет имен, хотя у $DATA и атрибутов, связанных с индексом, они обычно есть. Имена позволяют различать атрибуты файла, относящиеся к одному типу. Например, в файле с именованным потоком данных есть два атрибута $DATA: неименованный атрибут $DATA, хранящий неименованный по умолчанию поток данных, и именованный атрибут $DATA с именем дополнительного потока данных.

Имена файлов

NTFS и FAT допускают длину каждого имени файла в пути до 255 символов. Эти имена могут содержать Unicode-символы, точки и пробелы. Однако длина имен файлов в FAT, встроенной в MS-DOS, ограничена 8 символами (не-Unicode), за которыми следует расширение из трех символов, отделенное точкой. Рис. 12–27 иллюстрирует различные пространства имен файлов, поддерживаемые Windows, и показывает, как они перекрываются.

Рис. 12–27. Пространства имен файлов, поддерживаемые Windows

POSIX требует самого большого пространства имен из всех подсистем, поддерживаемых Windows. Поэтому пространство имен NTFS эквивалентно пространству имен POSIX. Подсистема POSIX может создавать имена, невидимые приложениям Windows и MS-DOS, в том числе имена с концевыми точками и концевыми пробелами. Создание файла в большом пространстве имен POSIX обычно не является проблемой, потому что вы создаете такой файл для использования подсистемой POSIX или ее клиентской системой.

Ho взаимосвязь между 32-разрядными Windows-приложениями и программами MS-DOS и 16-разрядной Windows намного теснее. Область, отведенная Windows на рис. 12–26, представляет имена файлов, которые подсистема Windows может создавать на томе NTFS, хотя такие имена невидимы программам MS-DOS и 16-разрядной Windows. B эту группу входят имена файлов, не соответствующие формату «8.3» (длинные имена, имена с Unicode-символами, с несколькими точками или начинающиеся с точки, а также имена с внутренними пробелами). При создании файла с таким именем NTFS автоматически генерирует для него альтернативное имя в стиле MS-DOS. Windows показывает такие имена при использовании команды dir с ключом /x.

Имена MS-DOS — полнофункциональные псевдонимы файлов NTFS и хранятся в том же каталоге, что и длинные имена. Ha рис. 12–28 показана запись MFT для файла с автоматически сгенерированным MS-DOS-именем.

Имя NTFS и сгенерированное имя MS-DOS хранятся в той же записи и относятся к одному и тому же файлу. Имя MS-DOS можно использовать для открытия, чтения, записи и копирования файла. Если пользователь переименовывает файл, заменяя длинное имя на краткое или наоборот, новое имя заменяет оба существовавших варианта. Если новое имя является недопустимым для MS-DOS, NTFS генерирует для файла другое MS-DOS-имя.

ПРИМЕЧАНИЕ Аналогичным образом реализуются жесткие связи POSIX. При создании жесткой связи с POSIX-файлом NTFS добавляет в запись MFT дополнительный атрибут имени файла. Однако эти две ситуации имеют одно отличие. Когда пользователь удаляет файл PO-SIX, у которого было несколько имен (жестких связей), запись о файле и сам файл остаются. Файл и его запись удаляются только после удаления последнего имени (жесткой связи). Если у файла есть и имя NTFS, и автоматически сгенерированное имя MS-DOS, пользователь может удалить файл по любому из этих имен.

Вот алгоритм, применяемый NTFS при генерации краткого MS-DOS-имени из длинного.

1. Удалить из длинного имени все символы, недопустимые в именах MS-DOS, включая пробелы и Unicode-символы. Удалить начальную и концевую точки, а также все внутренние точки, кроме последней.

2. Урезать часть строки перед точкой (если она есть) до шести символов и добавить строку «~n» (где n — порядковый номер, который начинается с 1; он нужен, чтобы различать файлы, урезание имен которых дает одинаковый результат). Урезать строку после точки (если она есть) до трех символов.

3. Преобразовать полученный набор символов в верхний регистр. MS-DOS нечувствительна к регистру букв в именах файлов, но эта операция гарантирует, что NTFS не сгенерирует для файла новое имя, отличающееся от старого лишь регистром.

4. Если сгенерированное имя дублирует уже имеющееся в каталоге, увеличить порядковый номер в строке «~n» на 1 (или на большее значение).

B таблице 12-5 показаны длинные имена файлов с рис. 12–26 и их MS-DOS-версии, сгенерированные NTFS. Приведенный выше алгоритм и примеры на рис. 12–26 должны дать вам представление об именах в стиле MS-DOS, генерируемых NTFS.

ПРИМЕЧАНИЕ Вы можете отключить генерацию кратких имен, присвоив параметру реестра HKLM\System\CurrentControlSet\Control\FileSystem\ NtfsDisable8dot3NameCreation значение типа DWORD, равное 1, хотя обычно это не рекомендуется (из-за вероятной несовместимости приложений, которые полагаются на такую функциональность).

Резидентные и нерезидентные атрибуты

Если файл невелик, все его атрибуты и их значения (например, файловые данные) умещаются в одной записи файла. Когда значение атрибута хранится непосредственно в MFT, атрибут называется резидентный (например, все атрибуты на рис. 12–27 являются резидентными). Некоторые атрибуты всегда резидентны — по ним NTFS находит нерезидентные атрибуты. Так, атрибуты «стандартная информация» и «корень индекса» всегда резидентны.

Каждый атрибут начинается со стандартного заголовка, в котором содержится информация об атрибуте, используемая NTFS для базового управления атрибутами. B заголовке, который всегда является резидентным, регистрируется, резидентно ли значение данного атрибута. B случае резидентных атрибутов заголовок также содержит смещение значения атрибута от начала заголовка и длину этого значения (рис. 12–29).

Когда значение атрибута хранится непосредственно в MFT, обращение к нему занимает значительно меньше времени. Вместо того чтобы искать файл в таблице, а затем считывать цепочку кластеров для поиска файловых данных (как, например, поступает FAT), NTFS обращается к диску только один раз и немедленно считывает данные.

Как видно из рис. 12–30, атрибуты небольшого каталога, а также небольшого файла, могут быть резидентными в MFT Для небольшого каталога атрибут «корень индекса» содержит индекс файловых ссылок на файлы и подкаталоги этого каталога.

Конечно, многие файлы и каталоги нельзя втиснуть в запись MFT с фиксированным размером в 1 Кб. Если некий атрибут, например файловые данные, слишком велик и не умещается в записи MFT, NTFS выделяет для него отдельные кластеры за пределами MFT Эта область называется группой (run) или экстентом (extent). Если размер значения атрибута впоследствии расширяется (например, при добавлении в файл дополнительных данных), NTFS выделяет для новых данных еще одну группу. Атрибуты, значения которых хранятся в группах, а не в MFT, называются нерезидентными. Файловая система сама решает, будет атрибут резидентным или нерезидентным, и обеспечивает пользовательским процессам прозрачный доступ к этим данным.

B случае нерезидентного атрибута (им может быть атрибут данных большого файла) в его заголовке содержится информация, нужная NTFS для поиска значения атрибута на диске. Ha рис. 12–31 показан нерезидентный атрибут данных, хранящийся в двух группах.

Рис. 12–31. Запись в МFТ для большого файла с двумя группами данных

Среди стандартных атрибутов нерезидентными бывают лишь те, чей размер может увеличиваться. Для файлов такими атрибутами являются данные и список атрибутов (не показанный на рис. 12–31). Атрибуты «стандартная информация» и «имя файла» всегда резидентны.

B большом каталоге также могут быть нерезидентные атрибуты (или части атрибутов), как видно из рис. 12–32. B этом примере в записи MFT не хватает места для хранения индекса файлов, составляющих этот большой каталог. Часть индекса хранится в атрибуте корня индекса, а остальное — в нерезидентных группах, называемых индексными буферами (index buffers). Атрибуты корня индекса, выделенной группы индексов (index allocation) и битовой карты показаны здесь в упрощенной форме (подробнее о них см. следующий раздел). Атрибуты стандартной информации и имени файла всегда резидентны. Заголовок и по крайней мере часть значения атрибута корня индекса в случае каталогов также резидентны.

Рис. 12–32. Запись MFT для большого каталога с нерезидентным индексом имен файлов

Когда атрибуты файла (или каталога) не умещаются в записи MFT и для них требуется отдельное место, NTFS отслеживает выделяемые группы посредством пар сопоставлений VCN-LCN. LCN представляют последовательность кластеров на всем томе, пронумерованных от 0 до n. VCN нумеруют от 0 до m только кластеры, принадлежащие конкретному файлу. Пример нумерации кластеров в группах нерезидентного атрибута данных приведен на рис. 12–33.

Рис. 12–33. VCN для нерезидентного атрибута данных

Если бы этот файл занимал больше двух групп, нумерация в третьей группе началась бы с VCN 8. Как показано на рис. 12–34, заголовок атрибута данных содержит сопоставления VCN-LCN для обоих групп, что позволяет NTFS легко находить выделенные под них области на диске.

Рис. 12–34. Сопоставления VCN-LCN для нерезидентного атрибута данных

Хотя на рис. 12–33 показаны только группы данных, в группах могут храниться и другие атрибуты, если они не умещаются в записи MFT Когда у файла так много атрибутов, что они не умещаются в записи MFT, для хранения дополнительных атрибутов (или заголовков в случае нерезидентных атрибутов) используется вторая запись MFT При этом добавляется атрибут, называемый списком ampuбymoe (attribute list). Список атрибутов содержит имя и код типа каждого атрибута файла, а также файловую ссылку на запись MFT, в которой находится данный атрибут. Атрибут «список атрибутов» предназначен для тех случаев, когда файл становится настолько большим или фраг-ментированным, что одной записи MFT уже недостаточно для хранения большого объема сведений о сопоставлениях VCN-LCN, нужных для поиска всех групп. Список атрибутов обычно нужен файлам, у которых более 200 групп.

Сжатие данных и разреженные файлы

NTFS поддерживает сжатие по отдельным файлам, по каталогам и по томам (NTFS сжимает только пользовательские данные, не трогая метаданные файловой системы). Выяснить, сжат ли том, можно через Windows-функцию GetVolumeInformation. Чтобы получить реальный размер сжатого файла, используйте Windows-функцию GetCompressedFileSize. Наконец, проверить или изменить параметры сжатия для файла или каталога позволяет Windows-функция DeviceIoControl (см. управляющие коды файловой системы FSCTL_ GET_COMPRESSION и FSCT_SET_COMPRESSION в описании этой функции в Platform SDK). Учтите, что изменение степени сжатия применительно к файлу выполняется немедленно, а применительно к каталогу или тому — нет. Bo втором случае степень сжатия, заданная для каталога или тома, становится степенью сжатия по умолчанию для всех новых файлов и подкаталогов, создаваемых в каталоге или на томе.

Следующий раздел является введением в сжатие данных в NTFS на примере простого случая компрессии разреженных данных. После него мы обсудим сжатие обычных и разреженных файлов.

Сжатие разреженных данных

Разреженными (sparse) называются данные (часто большого размера), в которых лишь малая часть отлична от нулевых значений. Пример разреженных данных — разреженная матрица. Как уже говорилось, для обозначения кластеров файла NTFS использует виртуальные номера кластеров (VCN) — от 0 до m. Каждый VCN соответствует логическому номеру кластера (LCN), который определяет местонахождение кластера на диске. Ha рис. 12–35 показаны группы (занимаемые участки дискового пространства) обычного (несжатого файла), а также их VCN и LCN.

Данный файл хранится в трех группах, каждая по 4 кластера, и таким образом занимает 12 кластеров. Запись MFT для этого файла представлена на рис. 12–36. Как мы уже отмечали, для экономии места на диске атрибут данных в записи MFT содержит только одно сопоставление VCN-LCN для каждой группы, а не для каждого кластера. Тем не менее каждому VCN от 0 до 11 сопоставлен свой LCN. Первый элемент начинается с VCN 0 и охватывает 4 кластера, второй начинается с VCN 4, также охватывая 4 кластера, и т. д. Такой формат типичен для несжатого файла.

Один из способов сжатия файла, применяемых NTFS, состоит в удалении из него длинных цепочек нулей. Если файл разрежен, он обычно сжимается до размера, составляющего лишь часть дискового пространства, необходимого для его хранения в нормальном виде. При последующей записи в этот файл NTFS выделяет пространство только для групп с ненулевыми данными.

Ha рис. 12–37 изображены группы сжатого разреженного файла. Обратите внимание на то, что для некоторых диапазонов VCN файла (16–31 и 64-127) дисковое пространство не выделено.

B записи MFT для этого сжатого файла пропущены блоки VCN кластеров, содержащих нули, т. е. для них не выделено дисковое пространство. Так, первый элемент данных на рис. 12–38 начинается с VCN 0 и охватывает 16 кластеров. Второй элемент перескакивает на VCN 32 и охватывает еще 16 кластеров.

Рис. 12–38. Запись MFT дпя сжатого файла, содержащего разреженные данные

Когда программа читает данные из сжатого файла, NTFS проверяет запись MFT, чтобы выяснить, имеется ли сопоставление VCN-LCN для считываемого участка файла. Если программа обращается в невыделенную «дыру» в файле, значит, данные этой части файла состоят из нулей, и тогда NTFS возвращает нули, не обращаясь к диску. Если программа записывает в «дыру» ненулевые данные, NTFS автоматически выделяет дисковое пространство и записывает туда эти данные. Такой метод очень эффективен для разреженных файлов, содержащих много нулевых данных.

Сжатие неразреженных данных

Предыдущий пример сжатия разреженного файла довольно надуманный. Он описывает «сжатие» в том случае, когда целые области файла заполнены нулями, но на остальные файловые данные сжатие не оказывает никакого влияния. B большинстве файлов данные не являются разреженными, тем не менее их можно компрессировать по какому-нибудь алгоритму сжатия.

B NTFS пользователи могут сжимать отдельные файлы или все файлы в каталоге. (Новые файлы, создаваемые в сжатом каталоге, сжимаются автоматически. Файлы, существовавшие в каталоге до его сжатия, должны быть сжаты индивидуально.) Сжимая файл, NTFS разбивает его необработанные данные на единицы сжатия (compression units) длиной по 16 кластеров. Некоторые последовательности данных в файле могут сжиматься недостаточно сильно или вообще не сжиматься, поэтому для каждой единицы сжатия NTFS определяет, будет ли при ее сжатии получен выигрыш хотя бы в один кластер. Если сжатие не позволяет освободить даже один кластер, NTFS выделяет 16-кластерную группу и записывает единицу сжатия на диск, не компрессируя ее данные. Если же данные можно сжать до 15 или менее кластеров, NTFS выделяет на диске ровно столько кластеров, сколько нужно для хранения сжатых данных, после чего записывает данные на диск. Рис. 12–39 иллюстрирует сжатие файла, состоящего из четырех групп. Незакрашенные области отражают дисковое пространство, которое файл будет занимать после сжатия. Первая, вторая и четвертая группы сжимаются, а третья — нет. Ho даже с одной несжатой группой достигается экономия 26 кластеров диска, т. е. длина файла уменьшается на 41 %.

ПРИМЕЧАНИЕ Хотя на схемах, приведенных в этой главе, показаны непрерывные LCN, единицы сжатия не обязательно располагаются в физически смежных кластерах. Группы, занимающие несмежные кластеры, заставляют NTFS создавать несколько более сложные записи MFT, чем показанные на рис. 12–39.

При записи данных в сжатый файл NTFS гарантирует, что каждая группа будет начинаться на виртуальной 16-кластерной границе. Таким образом, начальный VCN каждой группы кратен 16, и длина группы не превышает 16 кластеров. При работе со сжатым файлом NTFS единовременно считывает и записывает минимум одну единицу сжатия. Ho при записи сжатых данных NTFS пытается помещать единицы сжатия в физически смежные области, так чтобы их можно было считывать в ходе одной операции ввода-вывода. Размер единицы сжатия в 16 кластеров выбран для уменьшения внутренней фрагментации: чем больше размер единицы сжатия, тем меньше дискового пространства нужно для хранения данных. Размер единицы сжатия, равный l6 кластерам, основан на компромиссе между минимизацией размера сжатых файлов и замедлением операций чтения для программ, использующих прямой (произвольный) доступ к содержимому файлов. При каждом промахе кэша приходится декомпрессировать эквивалент l6 кластеров (вероятность промаха кэша при прямом доступе к файлу выше, чем при последовательном). Ha рис. 12–40 представлена запись MFT для сжатого файла, показанного на рис. 12–39.

Одно из различий между этим сжатым файлом и сжатым разреженным файлом из более раннего примера в том, что в данном случае три группы имеют длину менее 16 кластеров. Считывание этой информации из записи MFT позволяет NTFS определить, сжаты ли данные в этом файле. Каждая группа короче l6 кластеров содержит сжатые данные, которые NTFS должна разархивировать при первом чтении группы в кэш. Группа, длина которой равна точно 16 кластерам, не содержит сжатых данных, а значит, не требует декомпрессии.

Если группа содержит сжатые данные, NTFS разархивирует их во временный буфер и копирует в буфер вызывающей программы. Кроме того, NTFS помещает декомпрессированные данные в кэш, поэтому последующее чтение из этой группы выполняется так же быстро, как и простое чтение из кэша. Все изменения файла NTFS записывает в кэш, позволяя подсистеме отложенной записи асинхронно сжимать и записывать измененные данные на диск. Такая стратегия гарантирует, что запись в сжатый фарш вызовет примерно ту же задержку, что и запись в несжатый файл.

NTFS размещает сжатый файл на диске по возможности в смежных областях. Как указывают LCN, первые две группы сжатого файла на рис. 12–38 являются физически смежными, равно как и две последних. Если две и более группы расположены последовательно, NTFS выполняет опережающее чтение с диска — как и в случае обычных файлов. Поскольку чтение и декомпрессия непрерывных файловых данных выполняются асинхронно и еще до того, как программа запросит эти данные, то в последующих операциях чтения информация извлекается непосредственно из кэша, что значительно ускоряет процесс чтения.

Разреженные файлы

Разреженные файлы (тип файлов NTFS отличающийся от ранее описанных файлов, которые содержат разреженные данные) по сути являются сжатыми файлами, неразреженные данные которых NTFS не сжимает. Однако NTFS обрабатывает данные группы из записи MFT разреженного файла так же, как и в случае сжатых файлов, состоящих из разреженных и неразреженных данных.

Файл журнала изменений

Файл журнала изменений, \$Extend\$UsnJrnl, является разреженным файлом, который NTFS создает, только когда приложение активизирует регистрацию изменений. Журнал хранит записи изменений в потоке данных $J. Эти записи включают следующую информацию об изменениях файлов или каталогов:

• время изменения;

• тип изменения (удаление, переименование, увеличение размера и т. д.);

• атрибуты файла или каталога; • имя файла или каталога;

• номер файловой ссылки файла или каталога;

• номер файловой ссылки родительского каталога файла.

Поскольку журнал является разреженным, он никогда не переполняется; когда его размер на диске достигает определенного максимума, NTFS начинает просто обнулять файловые данные, предшествующие текущему блоку информации об изменениях, как показано на рис. 12–41. Ho, чтобы предотвратить постоянное изменение размера журнала, NTFS уменьшает его, только когда он в два раза превосходит установленный максимум.

Рис. 12–41. Выделение пространства для журнала изменений ($UsnJrnl)

Индексация

B NTFS каталог — это просто индекс имен файлов, т. е. совокупность имен файлов (с соответствующими файловыми ссылками), организованная таким образом, чтобы обеспечить быстрый доступ. Для создания каталога NTFS индексирует атрибуты «имя файла» из этого каталога. Запись MFT для корневого каталога тома показана на рис. 12–42.

C концептуальной точки зрения, элемент MFT для каталога содержит в своем атрибуте «корень индекса» отсортированный список файлов каталога. Ho для больших каталогов имена файлов на самом деле хранятся в индексных буферах размером по 4 Кб, которые содержат и структурируют имена файлов. Индексные буферы реализуют структуру данных типа «b+ tree», которая позволяет свести к минимуму число обращений к диску при поиске какого-либо файла, особенно в больших каталогах. Атрибут «корень индекса» содержит первый уровень структуры b+ tree (подкаталоги корневого каталога) и указывает на индексные буферы, содержащие следующий уровень (другие подкаталоги или файлы).

Ha рис. 12–42 в атрибуте «корень индекса» и индексных буферах показаны только имена файлов (например, файл 6), но каждая запись индекса содержит и файловую ссылку на запись MFT, описывающую данный файл, плюс метку времени и информацию о размере файла. NTFS дублирует метку времени и информацию о размере файла из записи MFT для файла. Такой подход, используемый файловыми системами FAT и NTFS, требует записи обновленной информации в два места. Ho даже при этом просмотр каталогов существенно ускоряется, поскольку файловая система может сообщать метки времени и размеры файлов, не открывая каждый файл в каталоге.

Атрибут «выделенная группа индексов» сопоставляет VCN групп индексных буферов с LCN, которые указывают, в каком месте диска находятся индексные буферы, а битовая карта используется для учета того, какие VCN в индексных буферах заняты, а какие свободны. Ha рис. 12–42 на каждый VCN, т. е. на каждый кластер, приходится по одной записи для файла, но на самом деле кластер содержит несколько записей. Каждый индексный буфер размером 4 Кб может содержать 20–30 записей для имен файлов.

Структура данных b+ tree — это разновидность сбалансированного дерева, идеальная для организации отсортированных данных, хранящихся на диске, так как позволяет минимизировать количество обращений к диску при поиске заданного элемента. B MFT атрибут корня индекса для каталога содержит несколько имен файлов, выступающих в качестве индексов для второго уровня b+ tree. C каждым именем файла в атрибуте корня индекса связан необязательный указатель индексного буфера. Этот индексный буфер содержит имена файлов, которые с точки зрения лексикографии меньше данного имени. Например, на рис. 12–42 файл 4 — это элемент первого уровня b+ tree. Он указывает на индексный буфер, содержащий имена файлов, которые лексикографически меньше имени в этом элементе, — файл 0, файл 1 и файл 3. Обратите внимание, что использованные в этом примере имена (файл1, файл2 и др.) не являются буквальными, — они просто иллюстрируют относительное размещение файлов, лексикографически упорядоченных в соответствии с показанной последовательностью.

Хранение имен файлов в структурах вида b+ tree дает несколько преимуществ. Поиск в каталоге выполняется быстрее, так как имена файлов хранятся в отсортированном порядке. A когда высокоуровневое программное обеспечение перечисляет файлы в каталоге, NTFS возвращает уже отсортированные имена. Наконец, поскольку b+ tree имеет тенденцию к росту в ширину, а не в глубину, скорость поиска не уменьшается с увеличением размера каталога.

Кроме индексации имен, NTFS обеспечивает универсальную индексацию данных, и некоторая функциональность NTFS (в том числе идентификации объектов, отслеживания квот и консолидированной защиты) использует индексацию для управления внутренними данными.

Идентификаторы объектов

Кроме идентификатора объекта, назначенного файлу или каталогу и хранящегося в атрибуте $OBJECT_ID записи MFT, NTFS также запоминает соответствие между идентификаторами объектов и номерами их файловых ссылок в индексе Ю файла метаданных \$Extend\$ObjId. Элементы индекса сортируются по значениям идентификатора объекта, благодаря чему NTFS может быстро находить файл по его идентификатору. Таким образом, используя недокументированную функциональность, приложения могут открывать файл или каталог по идентификатору объекта. Ha рис. 12–43 показана взаимосвязь между файлом метаданных $Objid и атрибутами $OBJECT_ID в MFT-записях.

Отслеживание квот

NTFS хранит информацию о квотах в файле метаданных \$Extend\$Quota, который состоит из индексов $O и $Q. Структура этих индексов показана на рис. 12–44. NTFS не только присваивает каждому дескриптору защиты уникальный внутренний идентификатор защиты, но и назначает каждому пользователю уникальный идентификатор. Когда администратор задает квоты для пользователя, NTFS создает идентификатор этого пользователя, соответствующий его SID. NTFS создает в индексе $O запись, сопоставляющую SID с идентификатором пользователя, и сортирует этот индекс по идентификаторам пользователей; в индексе $Q создается запись, управляющая квотами (quota control entry). Эта запись содержит лимиты, выделенные пользователю, а также объем дискового пространства, отведенный ему на данном томе.

Когда приложение создает файл или каталог, NTFS получает SID пользователя этого приложения и ищет соответствующий идентификатор пользователя в индексе $O. Этот идентификатор записывается в атрибут $STANDARD_INFORMATION нового файла или каталога. Затем NTFS просматривает запись квот в индексе $Q и определяет, не превышает ли выделенное дисковое пространство установленные для данного пользователя лимиты. Когда новое дисковое пространство, выделяемое пользователю, превышает пороговое значение, NTFS предпринимает соответствующие меры, например, записывает событие в журнал System (Система) или отклоняет запрос на создание файла или каталога.

Консолидированная защита

NTFS всегда поддерживала средства защиты, которые позволяют администратору указывать, какие пользователи могут обращаться к определенным файлам и каталогам, а какие — не могут. B версиях NTFS до Windows 2000 каждый файл и каталог хранит дескриптор защиты в своем атрибуте защиты. Ho в большинстве случаев администратор применяет одинаковые пара метры защиты к целому дереву каталогов, что приводит к дублированию дескрипторов защиты во всех файлах и подкаталогах этого дерева каталогов. B многопользовательских средах, например в Windows 2000 Server со службой Terminal Services, такое дублирование может потребовать слишком большого пространства на диске, поскольку дескрипторы защиты будут содержать элементы для множества учетных записей. NTFS в Windows 2000 и более поздних версиях OC оптимизируют использование дискового пространства дескрипторами защиты за счет применения централизованного файла метаданных $Secure, в котором хранится только один экземпляр каждого дескриптора защиты на данном томе.

Файл $Secure содержит два атрибута индексов ($SDH и $SIJ), а также атрибут потока данных $SDS, как показано на рис. 12–45. NTFS назначает каждому уникальному дескриптору защиты на томе внутренний для NTFS идентификатор защиты (не путать с SID, который уникально идентифицирует учетные записи компьютеров и пользователей) и хэширует дескриптор защиты по простому алгоритму. Хэш является потенциально неуникальным «стенографическим» представлением дескриптора. Элементы в индексе $SDH увязывают эти хэши с местонахождением дескриптора защиты внутри атрибута данных $SDS, а элементы индекса $SII сопоставляют NTFS-идентификаторы защиты с местонахождением дескриптора защиты в атрибуте данных $SDS.

Когда вы применяете дескриптор защиты к файлу или каталогу, NTFS получает хэш этого дескриптора и просматривает индекс $SDH, пытаясь найти совпадение. NTFS сортирует элементы индекса $SDH по хэшам дескрипторов защиты и хранит эти элементы в структуре вида b+ tree. Обнаружив совпадение для дескриптора в индексе $SDH, NTFS находит смещение дескриптора защиты от смещения элемента и считывает дескриптор из атрибута $SDS. Если хэши совпадают, а дескрипторы — нет, NTFS ищет следующее совпадение в индексе $SDH. Когда NTFS находит точное совпадение, файл или каталог, к которому вы применяете дескриптор защиты, может ссылаться на существующий дескриптор в атрибуте $SDS. Тогда NTFS считывает NTFS-идентифика-тор защиты из элемента $SDH и сохраняет его в атрибуте $STANDARD_ INFORMATION файла или каталога. Атрибут $STANDARD_INFORMATION, имеющийся у всех файлов и каталогов, хранит базовую информацию о файле, в том числе его атрибуты, временные метки и идентификатор защиты.

Если NTFS не обнаруживает в индексе $SDH элемент с дескриптором защиты, совпадающим с тем, который вы применяете, значит, ваш дескриптор уникален в пределах данного тома, и NTFS присваивает ему новый внутренний идентификатор защиты. Такие идентификаторы являются 32-битными значениями, но SID обычно в несколько раз больше, поэтому представление SID в виде NTFS-идентификаторов защиты экономит место в атрибуте $STANDARD_INFORMATION. NTFS включает дескриптор защиты в атрибут $SDS, который сортируется в структуру вида b+ tree по NTFS-идентификатору защиты, а потом добавляет его в элементы индексов $SDH и $SII, ссылающиеся на смещение дескриптора в данных $SDS.

Когда приложение пытается открыть файл или каталог, NTFS использует индекс $SII для поиска дескриптора защиты этого файла или каталога. NTFS считывает внутренний идентификатор защиты файла или каталога из атрибута SSTANDARD_INFORMATION записи MFT Затем по индексу $SII файла $Secure она находит элемент с нужным идентификатором в атрибуте $SDS. По смещению в атрибуте $SDS NTFS считывает дескриптор защиты и завершает проверку прав доступа. NTFS хранит последние 32 дескриптора защиты, к которым было обращение, вместе с их элементами $SII в кэше, чтобы впоследствии была возможность обращаться только к файлу $Secure.

NTFS не удаляет элементы в файле $Secure, даже если на них не ссылаются никакие файлы или каталоги на томе. Это приводит лишь к незначительному увеличению места, занимаемого файлом $Secure, так как на большинстве томов, даже если они используются уже весьма долго, уникальных дескрипторов защиты сравнительно немного.

Механизм универсальной индексации позволяет NTFS повторно использовать дескрипторы защиты для файлов и каталогов с одинаковыми параметрами защиты. По индексу $SII NTFS быстро находит дескриптор защиты в файле SSecure при проверках прав доступа, а по индексу $SDH определяет, хранится ли применяемый к файлу или каталогу дескриптор защиты в файле SSecure, и, если да, использует этот дескриптор повторно.

Точки повторного разбора

Как уже упоминалось, точка повторного разбора представляет собой блок данных повторного разбора, определяемых приложениями; длина такого блока может быть до 16 Кб. B нем содержится 32-битный тэг повторного разбора, который хранится в атрибуте $REPARSEPOINT файла или каталога. Всякий раз, когда приложение создает или удаляет точку повторного разбора, NTFS обновляет файл метаданных \$Extend\$Reparse, в котором она хранит элементы, идентифицирующие номера записей о файлах и каталогах с точками повторного разбора. Централизованное хранение записей позволяет NTFS предоставлять приложениям интерфейсы для перечисления либо всех точек повторного разбора на томе, либо только точек заданного типа, например точек монтирования (подробнее о точках монтирования см. главу 10). Файл \$Extend\$Reparse использует NTFS-механизм универсальной индексации, сортируя элементы файлов (в индексе с именем $R) по тэгам повторного разбора.

Поддержка восстановления в NTFS

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

Как уже упоминалось в разделе «Восстанавливаемость», NTFS использует схему на основе обработки транзакций. Эта стратегия гарантирует полное восстановление диска, которое производится исключительно быстро (за считанные секунды) даже в случае самых больших дисков. NTFS ограничивается восстановлением данных файловой системы, гарантируя, что пользователь по крайней мере никогда не потеряет весь том из-за повреждения файловой системы. Ho, если приложение не выполнило определенных действий, например не сбросило на диск кэшированные файлы, NTFS не гарантирует полное восстановление пользовательских данных в случае краха. Защита пользовательских данных на основе технологии обработки транзакций предусматривается в большинстве СУБД для Windows, например в Microsoft SQL Server. Microsoft не стала реализовать восстановление пользовательских данных на уровне файловой системы, так как приложения обычно поддерживают свои схемы восстановления, оптимизированные под тот тип данных, с которыми они работают.

B следующих разделах рассказывается об эволюции средств обеспечения надежности файловой системы, и в этом контексте вводится концепция восстанавливаемых файловых систем с подробным обсуждением схемы протоколирования транзакций, за счет которой NTFS регистрирует изменения в структурах данных файловой системы. Кроме того, мы поясним, как NTFS восстанавливает том в случае сбоя системы.

Эволюция архитектуры файловых систем

Создание восстанавливаемой файловой системы можно рассматривать как еще один шаг в эволюции архитектуры файловых систем. B прошлом были распространены два основных подхода к организации поддержки ввода-вывода и кэширования в файловых системах: точная запись (careful write) и отложенная (lazywrite). B файловых системах, которые разрабатывались для VAX/VMS (права на нее перешли от DEC к Compaq) и некоторых других закрытых операционных систем, использовался алгоритм точной записи, тогда как в файловой системе HPFS операционной системы OS/2 и большинстве файловых систем UNIX применялась схема отложенной записи.

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

Файловые системы с точной записью

B случае аварии операционной системы или сбоя электропитания операции ввода-вывода, выполняемые в данный момент, немедленно прерываются. B зависимости от того, какие операции ввода-вывода выполнялись и насколько далеко продвинулось их выполнение, такое прерывание может нарушить целостность файловой системы. B данном контексте нарушение целостности — это повреждение файловой системы: например, имя файла появляется в списке каталога, но файловая система не знает, где он находится, или не может обратиться к его содержимому. B самых тяжелых случаях повреждение файловой системы может привести к потере всего тома.

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

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

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

ПРИМЕЧАНИЕ Файловая система FAT в MS-DOS использует алгоритм сквозной записи, при котором обновления записываются на диск немедленно. B отличие от точной записи этот метод не требует от файловой системы упорядочения операций вывода для предотвращения нарушения целостности.

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

Файловые системы с отложенной записью

Файловая система с точной записью жертвует производительностью ради надежности. Она повышает производительность за счет стратегии кэширования с обратной записью (write-back caching); иными словами, изменения файла записываются в кэш, и содержимое последнего сбрасывается на диск оптимизированным способом, обычно в фоновом режиме.

Метод кэширования по алгоритму отложенной записи дает несколько преимуществ, увеличивающих производительность. Во-первых, уменьшается общее число операций записи на диск. Поскольку не требуется упорядоченных, немедленно осуществляемых операций вывода, содержимое буфера может измениться несколько раз, прежде чем оно будет записано на диск. Во-вторых, резко возрастает скорость обслуживания запросов приложений, поскольку файловая система может вернуть управление вызывающей программе, не дожидаясь завершения записи на диск. Наконец, стратегия отложенной записи игнорирует промежуточные несогласованные состояния тома, возникающие, когда несколько запросов на ввод-вывод перекрывается во времени. Это упрощает создание многопоточной файловой системы, допускающей одновременное выполнение нескольких операций ввода-вывода.

Недостаток метода отложенной записи состоит в том, что при его использовании бывают периоды, в течение которых том находится в настолько несогласованном состоянии, что файловая система не сможет исправить его в случае аварии. Следовательно, файловые системы с отложенной записью должны постоянно отслеживать состояние тома. B общем случае отложенная запись дает выигрыш в производительности по сравнению с точной записью — за счет большего риска и неудобств для пользователя при сбое системы.

Восстанавливаемые файловые системы

Восстанавливаемая файловая система типа NTFS превосходит по надежности файловые системы с точной записью и при этом достигает уровня производительности файловых систем с отложенной записью. Восстанавливаемая файловая система гарантирует сохранение целостности тома; с этой целью используется журнал изменений, изначально созданный для обработки транзакций. B случае аварии операционной системы такая файловая система восстанавливает целостность, выполняя процедуру восстановления на основе информации из файла журнала. Так как файловая система регистрирует все операции записи на диск в журнале, восстановление занимает несколько секунд независимо от размера тома.

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

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

Ни точная, ни отложенная запись не гарантирует защиты пользовательских данных. Если сбой системы произошел в тот момент, когда приложение выполняло запись в файл, последний может быть утерян или разрушен. Хуже того, сбой может повредить файловую систему с отложенной записью, разрушив существующие файлы или даже сделав всю информацию на томе недоступной.

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

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

Протоколирование

Восстанавливаемость NTFS обеспечивается методикой обработки транзакций, называемой протоколированием (logging). Прежде чем выполнить над содержимым диска подоперации какой-либо транзакции, изменяющей важные структуры данных файловой системы, NTFS регистрирует ее в файле журнала. Таким образом, в случае сбоя системы незавершенные транзакции можно повторить или отменить после перезагрузки компьютера. B технологии обработки транзакций эта методика называется опережающим протоколированием (write-ahead logging). B NTFS транзакции, к которым относятся, в частности, запись на диск или удаление файла, могут состоять из нескольких подопераций.

Сервис файла журнала

Сервис файла журнала (log file service, LFS) — это набор процедур режима ядра, локализованных в драйвере NTFS, который она использует для доступа к файлу журнала. Хотя LFS изначально был разработан для того, чтобы предоставлять средства протоколирования и восстановления более чем одному клиенту, он используется только NTFS. Вызывающая программа, в данном случае NTFS, передает LFS указатель на открытый объект «файл», который определяет файл, выступающий в роли журнала. LFS либо инициализирует новый журнал, либо вызывает диспетчер кэша для доступа к существующему журналу через кэш, как показано на рис. 12–46.

LFS делит файл журнала на две части: область перезапуска (restart area) и «безразмерную» область протоколирования (logging area) (рис. 12–47).

NTFS вызывает LFS для чтения и записи области перезапуска. B этой области NTFS хранит информацию о контексте, например позицию в области протоколирования, откуда начнется чтение при восстановлении после сбоя системы. Ha тот случай, если область перезапуска будет разрушена или по каким-либо причинам станет недоступной, LFS создает ее копию. Остальная часть журнала транзакций — это область протоколирования, в которой находятся записи транзакций, обеспечивающие восстановление тома после сбоя. LFS создает иллюзию бесконечности журнала транзакций за счет его циклического повторного использования (в то же время не перезаписывая нужную информацию). Для идентификации записей, помещенных в журнал, LFS использует номера логической последовательности (logical sequence number, LSN). Циклически используя журнал, LFS увеличивает значения LSN. NTFS представляет LSN в виде 64-битных чисел, поэтому число возможных LSN настолько велико, что практически может считаться бесконечным.

NTFS никогда не выполняет чтение/запись транзакций в журнал напрямую. LFS предоставляет сервисы, которые NTFS вызывает для открытия файла журнала, помещения в него записей, чтения записей из журнала в прямом и обратном порядке, сброса записей журнала до заданного LSN или установки логического начала журнала на больший LSN. B процессе восстановления NTFS вызывает LFS для чтения записей журнала в прямом направлении, чтобы повторить все транзакции, которые запротоколированы в журнале, но не записаны на диск в момент сбоя. NTFS также обращается к LFS для чтения записей в обратном направлении, чтобы отменить (или откатить) все транзакции, не полностью запротоколированные перед аварией системы, и установить начало файла журнала на запись с большим LSN после того, как старые записи журнала стали не нужны.

Вот как система обеспечивает восстановление тома.

1. Сначала NTFS вызывает LFS для записи в кэшируемый файл журнала любых транзакций, модифицирующих структуру тома.

2. NTFS модифицирует том (также в кэше).

3. Диспетчер кэша сообщает LFS сбросить файл журнала на диск. (Этот сброс реализуется LFS путем обратного вызова диспетчера кэша с указанием страниц памяти, подлежащих выводу на диск; см. рис. 12–46.)

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

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

Типы записей журнала

LFS позволяет своим клиентам помещать в журналы транзакций записи любого типа. NTFS использует несколько типов записей, два из которых — записи модификации (update records) и записи контрольной точки (checkpoint records) — будут рассмотрены в этом разделе.

Записи модификации

K ним относится большинство записей, которые NTFS помещает в журнал транзакций. Каждая такая запись содержит два вида информации.

• Информация для повтора (redo information) Описывает, как вновь применить к дисковому тому одну подоперацию полностью запротоколированной транзакции, если сбой системы произошел до того, как транзакция была переписана из кэша на диск.

• Информация для отмены (undo information) Описывает, как обратить изменения, вызванные одной подоперацией транзакции, которая в момент сбоя была запротоколирована лишь частично. Ha рис. 12–48 показаны три записи модификации в файле журнала. Каждая запись представляет одну подоперацию транзакции, создающей новый файл. Информация для повтора в каждой записи модификации сообщает NTFS, как повторно применить данную подоперацию к дисковому тому, а информация для отмены — как откатить (отменить) эту подоперацию.

После протоколирования транзакции (в данном примере — вызовом LFS до помещения трех записей модификации в файл журнала) NTFS выполняет в кэше подоперации этой транзакции, изменяющие том. Закончив обновление кэша, NTFS помещает в журнал еще одну запись, которая помечает всю транзакцию как завершенную. Эта подоперация известна как фиксация транзакции (committing transaction). После фиксации транзакции NTFS гарантирует, что все вызванные ею модификации будут отражены на томе, даже если после фиксации произойдет сбой операционной системы.

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

После повтора всех зафиксированных транзакций NTFS отыскивает в журнале такие, которые не были зафиксированы к моменту сбоя, и откатывает каждую запротоколированную подоперацию. B случае, представленном на рис. 12–48, NTFS вначале должна была бы отменить подоперацию T1c, после чего перейти по указателям назад и отменить T1b. Переход по указателям в обратном направлении и отмена подопераций продолжались бы до тех пор, пока NTFS не достигла бы первой подоперации транзакции. Следуя указателям, NTFS определяет, сколько и какие записи модификации нужно отменить для того, чтобы откатить транзакцию.

Информация для повтора и отмены может быть выражена либо физически, либо логически. Физическое описание задает модификации тома как диапазоны байтов на диске, которые следует изменить, переместить и т. д., а логическое — представляет модификации в терминах операций, например «удалить файл A.dat». Как самый низкий уровень программного обеспечения, поддерживающего структуру файловой системы, NTFS использует записи модификации с физическими описаниями. Однако для программного обеспечения обработки транзакций и других приложений записи модификации в логическом виде могут быть удобнее, поскольку логическое представление обновлений тома компактнее физических. Логическое описание требует участия NTFS в выполнении действий, связанных с такими операциями, как удаление файла.

NTFS генерирует записи модификации (обычно несколько) для каждой из следующих транзакций:

• создание файла;

• удаление файла;

• расширение файла;

• урезаниефайла;

• установка файловой информации;

• переименование файла;

• изменение прав доступа к файлу

Информация для повтора и отмены в записи модификации должна быть очень точной, иначе при отмене транзакции, восстановлении после сбоя системы или даже в ходе нормальной работы NTFS может попытаться повторить транзакцию, которая уже выполнена, или, наоборот, отменить транзакцию, которая никогда не выполнялась либо уже отменена. Аналогичным образом NTFS может попытаться повторить или отменить транзакцию, включающую нескольких записей модификации, которые не все были применены к диску. Формат записей модификации должен гарантировать, что лишние операции повтора или отмены будут идемпотентными (idempo-tent), т. е. дадут нейтральный эффект. Например, установка уже установленного бита, не оказывает никакого действия, но изменение на противоположное значения бита, которое уже изменено, — оказывает. Файловая система также должна корректно обрабатывать переходные состояния тома.

Записи контрольной точки

Помимо записей модификации NTFS периодически помещает в файл журнала запись контрольной точки, как показано на рис. 12–49.

Запись контрольной точки помогает NTFS определить, какая обработка нужна для восстановления тома, если сбой произошел сразу после добавления этой записи в журнал. Благодаря записи контрольной точки NTFS, например, знает, как далеко назад ей нужно пройти по журналу, чтобы начать восстановление. Добавив новую запись контрольной точки, NTFS записывает ее LSN в область перезапуска, так что, начиная восстановление после сбоя системы, она быстро находит самую последнюю запись контрольной точки.

Хотя LFS представляет NTFS, будто журнал транзакций безразмерен, на самом деле он не бесконечен. Значительный размер журнала транзакций и частая вставка записей контрольной точки (операция, обычно освобождающая место в файле журнала) делают вероятность его переполнения достаточно малой. Тем не менее LFS учитывает такую возможность, отслеживая несколько значений:

• размер свободного пространства в журнале;

• размер пространства, необходимого для добавления в журнал следующей записи и для отмены этого действия (если это вдруг потребуется);

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

Если в журнале не хватает места для суммы последних двух значений из списка, LFS сообщает об ошибке переполнения файла журнала, и NTFS генерирует исключение. Обработчик исключений NTFS откатывает текущую транзакцию и помещает ее в очередь для последующего перезапуска.

Чтобы освободить пространство в журнале транзакций, NTFS должна временно приостановить ввод-вывод в системе. Для этого она блокирует создание и удаление файлов, после чего запрашивает монопольный доступ ко всем системным файлам и разделяемый доступ ко всем пользовательским файлам. Постепенно активные транзакции либо завершаются успешно, либо вызывают исключение переполнения файла журнала. B последнем случае NTFS откатывает их, а затем помещает в очередь.

Заблокировав вышеописанным способом выполнение транзакций при операциях над файлами, NTFS вызывает диспетчер кэша для сброса на диск еще не записанных туда данных, в том числе данных файла журнала. После того как все успешно записано на диск, данные в журнале NTFS становятся ненужными. NTFS устанавливает начало журнала на текущую позицию, что делает журнал «пустым». Затем NTFS перезапускает транзакции, поставленные ранее в очередь. Так что за исключением короткой паузы в обработке ввода-вывода ошибка переполнения файла журнала не оказывает влияния на выполняемые программы.

Описанный сценарий — один из примеров того, как NTFS использует файл журнала не только для восстановления файловой системы, но и для исправления ошибок при нормальной работе.

Восстановление

NTFS автоматически выполняет восстановление диска при первом обращении к нему какой-либо программы после загрузки системы. (Если восстановление не требуется, весь процесс тривиален.) При восстановлении используются две таблицы, которые NTFS поддерживает в памяти.

• Таблица транзакций (transaction table) — предназначена для отслеживания начатых, но еще не зафиксированных транзакций. B процессе восстановления результаты подопераций этих транзакций должны быть удалены с диска.

• Таблица измененных страниц (dirty page table) — в нее записывается информация о том, какие страницы кэша содержат изменения структуры файловой системы, еще не записанные на диск. Эти данные в процессе восстановления должны быть сброшены на диск.

Каждые 5 секунд NTFS добавляет в файл журнала транзакций запись контрольной точки. Непосредственно перед этим она обращается к LFS для сохранения в журнале текущей копии таблицы транзакций и таблицы измененных страниц. Затем NTFS запоминает в записи контрольной точки LSN записей журнала, содержащих копии таблиц. B начале процесса восстановления после сбоя NTFS обращается к LFS для поиска записей журнала транзакций, содержащих самую последнюю запись контрольной точки и самые последние копии упомянутых выше таблиц. Затем NTFS копирует эти таблицы в память.

Обычно за последней записью контрольной точки в журнале транзакций находится еще несколько записей модификации. Эти записи соответствуют обновлениям тома, произошедшим после того, как запись контрольной точки была помещена в журнал. NTFS должна обновить таблицу транзакций и таблицу измененных страниц, включив в них информацию из этих дополнительных записей. После обновления таблиц NTFS использует их, а также содержимое журнала транзакций для обновления собственно тома.

При восстановлении тома NTFS выполняет три прохода по журналу транзакций, загружая его в память на первом проходе, чтобы минимизировать объем дискового ввода-вывода. Каждый проход имеет свое назначение:

• анализ;

• повтор транзакций; • отмена транзакций.

Проход анализа

При проходе анализа (analysis pass) NTFS просматривает журнал транзакций в прямом направлении, начиная с последней операции контрольной точки, чтобы найти записи модификации и обновить скопированные ранее в память таблицы транзакций и измененных страниц. Обратите внимание, что операция контрольной точки помещает в журнал транзакций три записи, между которыми могут оказаться записи модификации (рис. 12–50). NTFS должна приступить к сканированию с начала операции контрольной точки.

Большинство записей модификации, расположенных в журнале после начала операции контрольной точки, представляет собой изменение либо таблицы транзакций, либо таблицы измененных страниц. Например, если запись модификации относится к фиксации транзакции, то транзакция, которую представляет данная запись, должна быть удалена из таблицы транзакций. Аналогично, если запись модификации относится к обновлению страницы, изменяющему структуру данных файловой системы, нужно внести соответствующую поправку в таблицу измененных страниц.

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

Проход повтора

Ha проходе повтора (redo pass) NTFS сканирует журнал транзакций в прямом направлении, начиная с LSN самой старой записи, которая была обнаружена на проходе анализа (рис. 12–51). Она ищет записи модификации, относящиеся к обновлению страницы и содержащие модификации тома, которые были запротоколированы до сбоя системы, но не сброшены из кэша на диск. NTFS повторяет эти обновления в кэше.

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

Проход отмены

Завершив проход повтора, NTFS начинает npoxoд отмены (undo pass), откатывая транзакции, не зафиксированные к моменту сбоя системы. Ha рис. 12–52 показаны две транзакции в журнале: транзакция 1 зафиксирована до сбоя системы, а транзакция 2 — нет. NTFS должна отменить транзакцию 2.

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

B таблице транзакций NTFS для каждой незавершенной транзакции хранится LSN записи модификации, помещенной в журнал последней. B данном примере таблица транзакций сообщает, что для транзакции 2 это запись с LSN 4049. NTFS выполняет откат транзакции 2, как показано на рис. 12–53 (справа налево).

Найдя LSN 4049, NTFS извлекает информацию для отмены и выполняет отмену, сбрасывая биты 3–9 в своей битовой карте. Затем NTFS следует по обратному указателю к LSN 4048, который указывает ей удалить новое имя файла из индекса имен файлов. Наконец, NTFS переходит по последнему обратному указателю и освобождает запись MFT, зарезервированную для данного файла, в соответствии с информацией из записи модификации с LSN 4046. Ha этом откат транзакции 2 закончен. Если имеются другие незавершенные транзакции, NTFS повторяет ту же процедуру для их отката. Поскольку отмена транзакций изменяет структуру файловой системы на томе, NTFS должна протоколировать операцию отмены в журнале транзакций. B конце концов, при восстановлении может снова произойти сбой питания, и NTFS придется выполнить повтор операций отмены!

Когда проход отмены завершен, том возвращается в согласованное состояние. B этот момент NTFS сбрасывает на диск изменения кэша, чтобы гарантировать правильность содержимого тома. Далее NTFS записывает «пустую» область перезапуска, указывающую, что том находится в согласованном состоянии и что, если система сразу потерпит еще одну аварию, никакого восстановления не потребуется. Ha этом восстановление заканчивается.

NTFS гарантирует, что восстановление вернет том в некое существовавшее ранее целостное состояние, но не обязательно в непосредственно предшествовавшее сбою. NTFS не может дать такой гарантии, поскольку для большей производительности она использует алгоритм отложенной фиксации (lazy commit), а значит, сброс журнала транзакций из кэша на диск не выполняется немедленно всякий раз, когда добавляется запись «транзакция зафиксирована». Вместо этого несколько записей фиксации транзакций объединяются в пакет и записываются совместно — либо когда диспетчер кэша вызывает LFS для сброса журнала на диск, либо когда LFS помещает в журнал новую запись контрольной точки (каждые 5 секунд). Другая причина, по которой том не всегда возвращается к самому последнему состоянию, — в момент сбоя системы могли быть активны несколько параллельных транзакций, и одни записи фиксации этих транзакций были перенесены на диск, а другие — нет. Согласованное состояние тома, полученное в результате восстановления, отражает только те транзакции, чьи записи фиксации успели попасть на диск.

NTFS применяет журнал транзакций не только для восстановления тома, но и для других целей, реализация которых становится возможной за счет протоколирования транзакций. Файловые системы обязательно включают большой объем кода для обработки ошибок, возникающих в процессе обычного файлового ввода-вывода. Поскольку NTFS протоколирует каждую транзакцию, модифицирующую структуру тома, она может использовать журнал транзакций для восстановления после ошибок файловой системы и таким образом существенно упростить код обработки ошибок. Ошибка переполнения журнала транзакций, описанная ранее, — один из примеров использования протоколирования транзакций для обработки ошибок.

Большинство ошибок ввода-вывода, получаемых программой, не является ошибками файловой системы, и поэтому NTFS не может исправить их самостоятельно. Например, получив запрос на создание файла, NTFS может начать с создания записи в MFT, после чего ввести имя файла в индекс каталога. Однако при попытке выделить по своей битовой карте пространство для нового файла она может обнаружить, что диск заполнен, и запрос на создание файла удовлетворить не удастся. Тогда NTFS использует информацию из журнала транзакций для отмены уже выполненной части операции и освобождения структур данных, зарезервированных ею для файла. Затем ошибка «диск заполнен» возвращается вызывающей программе, которая и должна предпринять соответствующие действия.

Восстановление плохих кластеров в NTFS

Диспетчеры томов Windows — FtDisk (для базовых дисков) и LDM (для динамических) — могут восстанавливать данные из плохого (аварийного) сектора на отказоустойчивом томе, но, если жесткий диск не является SCSI-диском или если на нем больше нет резервных секторов, они не в состоянии заменить плохой сектор новым (подробнее о диспетчерах томов см. главу 10). B таком случае, когда файловая система считывает данные из плохого сектора, диспетчер томов восстанавливает информацию и возвращает ее вместе с соответствующим предупреждением.

Файловая система FAT никак не обрабатывает это предупреждение. Более того, ни эта файловая система, ни диспетчеры томов не ведут учет плохих секторов, поэтому, чтобы диспетчер томов не повторял все время восстановление данных из плохого сектора, пользователь должен запустить утилиту Chkdsk или Format. Обе эти утилиты далеко не идеальны в исключении плохого сектора из дальнейшего использования. Chkdsk требует много времени для поиска и удаления плохих секторов, a Format уничтожает все данные в форматируемом разделе.

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

Ha рис. 12–54 показана запись MFT для пользовательского файла, в одном из групп которого имеется плохой сектор. Когда NTFS получает ошибку, связанную с плохим сектором, она присоединяет содержащий его кластер к своему файлу плохих кластеров. Это предотвращает повторное выделение данного кластера другому файлу. Затем NTFS выделяет новый кластер и изменяет сопоставление VCN-LCN для файла так, чтобы оно указывало на этот кластер. Данная процедура, известная как переназначение плохого кластера (bad-cluster remapping), иллюстрируется на рис. 12–55. Кластер номер 1357, содержащий плохой сектор, заменяется новым кластером с номером 1049.

Стандартная

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

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

Как и другие файловые системы, NTFS не может восстановить данные из плохого сектора без помощи диспетчера томов. Однако она значительно сокращает ущерб, который наносится появлением плохого сектора. Если NTFS обнаруживает плохой сектор в ходе операции чтения, она переназначает кластер, как показано на рис. 12–55. Если том не сконфигурирован для избыточного хранения информации, NTFS возвращает ошибку чтения вызывающей программе. Хотя данные, находившиеся в этом кластере, теряются, остаток файла и сама файловая система сохраняются, вызывающая программа может соответствующим образом отреагировать на потерю данных, а плохой кластер больше не будет использоваться при распределении пространства на томе. Если NTFS обнаруживает плохой кластер во время записи, а не чтения, она переназначает его до выполнения операции записи и тем самым вообще избегает потери данных.

Ta же процедура восстановления используется, когда в аварийном секторе хранятся данные файловой системы. Если он находится на томе с избыточностью данных, NTFS динамически заменяет соответствующий кластер, используя данные, восстановленные диспетчером томов. Если том не избыточный, восстановить данные нельзя, и NTFS устанавливает в файле тома бит, указывающий, что том поврежден. При перезагрузке системы NTFS-утилита Chkdsk проверяет этот бит и, если он установлен, исправляет повреждение файловой системы, реконструируя метаданные NTFS.

Крайне редко повреждение файловой системы может произойти даже на отказоустойчивом томе: двойная ошибка способна разрушить и данные файловой системы, и информацию для их восстановления. Если авария системы происходит в тот момент, когда NTFS сохраняет, например, зеркальную копию записи MFT, индекса имен файлов или журнала транзакций, то зеркальная копия этих данных файловой системы обновляется не полностью. Если после перезагрузки системы ошибка, связанная с плохим сектором, возникает в том же месте основного диска, где находится частично записанная зеркальная копия, NTFS не может восстановить информацию с зеркального диска. Для детекции таких повреждений системных данных в NTFS реализована специальная схема. Если обнаружено нарушение целостности, в файле тома устанавливается бит повреждения, в результате чего при следующей загрузке системы метаданные NTFS будут реконструированы Chkdsk. Так как в отказоустойчивой дисковой конфигурации повреждение данных файловой системы маловероятно, потребность в Chkdsk возникает редко. Эта утилита позиционируется как дополнительная мера предосторожности, а не как основное средство восстановления информации.

Использование Chkdsk в NTFS существенно отличается от ее использования в FAT Перед записью на диск FAT устанавливает бит изменения тома, который сбрасывается по завершении модификации тома. Если сбой системы происходит при выполнении операции вывода, бит остается установленным, и после перезагрузки машины запускается Chkdsk. B NTFS утилита Chkdsk запускается, только когда обнаруживаются неожиданные или нечитаемые данные файловой системы и NTFS не может восстановить их с избыточного тома или из избыточных структур данных файловой системы на обычном томе. (Система дублирует загрузочный сектор, равно как и части MFT, необходимые для загрузки системы и выполнения процедуры восстановления NTFS. Эта избыточность гарантирует, что NTFS всегда сможет загрузиться и восстановить сама себя.)

B таблицу 12-6 сведены данные о том, что происходит при появлении плохого сектора на дисковом томе (отформатированном для одной из файловых систем Windows) в различных ситуациях, описанных в данном разделе.

1. Диспетчер томов не может заменять секторы ни в одном из следующих случаев: 1) жесткие диски, отличные от SCSI, не поддерживают стандартный интерфейс для замены секторов; 2) некоторые жесткие диски не имеют аппаратной поддержки замены секторов, а SCSI-диски, у которых она есть, могут со временем исчерпать весь резерв секторов.

2. Отказоустойчивым является том одного из следующих типов: зеркальный или RAID-5. При записи данные не теряются: NTFS переназначает кластер до выполнения записи.

Если том, на котором появился плохой сектор, сконфигурирован как отказоустойчивый, а жесткий диск поддерживает замену секторов и его запас резервных секторов еще не исчерпан, то тип файловой системы (FAT или NTFS) не имеет значения. Диспетчер томов заменяет плохой сектор, не требуя вмешательства со стороны пользователя или файловой системы.

Если плохой сектор появился на жестком диске, не поддерживающем замену секторов, то за переназначение плохого сектора или, как в случае NTFS, кластера, в котором находится плохой сектор, отвечает файловая система. FAT не умеет переназначать секторы или кластеры.

Механизм EFS

EFS (Encrypting File System) использует средства поддержки шифрования. При первом шифровании файла EFS назначает учетной записи пользователя, шифрующего этот файл, криптографическую пару — закрытый и открытый ключи. Пользователи могут шифровать файлы с помощью Windows Explorer; для этого нужно открыть диалоговое окно Properties (Свойства) применительно к нужному файлу, щелкнуть кнопку Advanced (Другие) и установить флажок Encrypt Contents To Secure Data (Шифровать содержимое для защиты данных), как показано на рис. 12–56. Пользователи также могут шифровать файлы с помощью утилиты командной строки cipher. Windows автоматически шифрует файлы в каталогах, помеченных зашифрованными. При шифровании файла EFS генерирует случайное число, называемое шифровальным ключом файла (file encryption key, FEK). EFS использует FEK для шифрования содержимого файла по более стойкому варианту DES (Data Encryption Standard) — DESX (в Windows 2000), а также no DESX, 3DES (Triple-DES) или AES (Advanced Encryption Standard) в Windows XP (Service Pack 1 и выше) и Windows Server 2003. EFS сохраняет FEK вместе с самим файлом, но FEK шифруется по алгоритму RSA-шифрования на основе открытого ключа. После выполнения EFS этих действий файл защищен: другие пользователи не смогут расшифровать данные без расшифрованного FEK файла, a FEK они не смогут расшифровать без закрытого ключа пользователя — владельца файла.

Стойкость алгоритмов шифрования FEK

По умолчанию FEK шифруется в Windows 2000 и Windows XP по алгоритму DESX, а в Windows XP с Service Pack 1 (или выше) и Windows Server 2003 — по алгоритму AES. B версиях Windows, разрешенных к экспорту за пределы США, драйвер EFS реализует 56-битный ключ шифрования DESX, тогда как в версии, подлежащей использованию только в США, и в версиях с пакетом для 128-битного шифрования длина ключа DESX равна 128 битам. Алгоритм AES в Windows использует 256-битные ключи. Применение 3DES разрешает доступ к более длинным ключам, поэтому, если вам требуется более высокая стойкость FEK, вы можете включить шифрование 3DES одним из двух способов: как алгоритм шифрования для всех криптографических сервисов в системе или только для EFS.

Чтобы 3DES стал алгоритмом шифрования для всех системных криптографических сервисов, запустите редактор локальной политики безопасности, введя secpol.msc в диалоговом окне Run (Запуск программы), и откройте узел Security Options (Параметры безопасности) под Local Policies (Локальные политики). Найдите параметр System Cryptography: Use FIPS Compliant Algorithms For Encryption, Hashing And Signing (Системная криптография: использовать FIPS-совмести-мые алгоритмы для шифрования, хеширования и подписывания) и включите его.

Чтобы активизировать 3DES только для EFS, создайте DWORD-параметр HKLM\SOFWARE\Microsoft\Windows NT\CurrentVersion\EFS\ AlgorithmID, присвойте ему значение 0x6603 и перезагрузите систему.

Рис. 12–56. Шифрование файлов через пользовательский интерфейс Windows Explorer

Для шифрования FEK используется алгоритм криптографической пары, а для шифрования файловых данных — DESX, AES или 3DES (все это алгоритмы симметричного шифрования, в которых применяется один и тот же ключ для шифрования и дешифрования). Как правило, алгоритмы симметричного шифрования работают очень быстро, что делает их подходящими для шифрования больших объемов данных, в частности файловых. Однако у алгоритмов симметричного шифрования есть одна слабая сторона: зашифрованный ими файл можно вскрыть, получив ключ. Если несколько человек собирается пользоваться одним файлом, защищенным только DESX, AES или 3DES, каждому из них понадобится доступ к FEK файла. Очевидно, что незашифрованный FEK — серьезная угроза безопасности. Ho шифрование FEK все равно не решает проблему, поскольку в этом случае нескольким людям приходится пользоваться одним и тем же ключом расшифровки FEK.

Защита FEK ~ сложная проблема, для решения которой EFS использует ту часть своей криптографической архитектуры, которая опирается на технологии шифрования с открытым ключом. Шифрование FEK на индивидуальной основе позволяет нескольким лицам совместно использовать зашифрованный файл. EFS может зашифровать FEK файла с помощью открытого ключа каждого пользователя и хранить их FEK вместе с файлом. Каждый может получить доступ к открытому ключу пользователя, но никто не сможет расшифровать с его помощью данные, зашифрованные по этому ключу. Единственный способ расшифровки файла заключается в использовании операционной системой закрытого ключа. Закрытый ключ помогает расшифровать нужный зашифрованный экземпляр FEK файла. Алгоритмы на основе открытого ключа обычно довольно медленные, поэтому они используются EFS только для шифрования FEK. Разделение ключей на открытый и закрытый немного упрощает управление ключами по сравнению с таковым в алгоритмах симметричного шифрования и решает дилемму, связанную с защитой FEK.

Windows хранит закрытые ключи в подкаталоге Application Data\Micro-soft\Crypto\RSA каталога профиля пользователя. Для защиты закрытых ключей Windows шифрует все файлы в папке RSA на основе симметричного ключа, генерируемого случайным образом; такой ключ называстся мастер-ключом пользователя. Мастер-ключ имеет длину в 64 байта и создается стойким генератором случайных чисел. Мастер-ключ также хранится в профиле пользователя в каталоге Application Data\Microsoft\Protect и зашифровывается по алгоритму 3DES с помощью ключа, который отчасти основан на пароле пользователя. Когда пользователь меняет свой пароль, мастер-ключи автоматически расшифровываются, а затем заново зашифровываются с учетом нового пароля.

Функциональность EFS опирается на несколько компонентов, как видно на схеме архитектуры EFS (рис. 12–57). B Windows 2000 EFS реализована в виде драйвера устройства, работающего в режиме ядра и тесно связанного с драйвером файловой системы NTFS, но в Windows XP и Windows Server 2003 поддержка EFS включена в драйвер NTFS. Всякий раз, когда NTFS встречает шифрованный файл, она вызывает функции EFS, зарегистрированные кодом EFS режима ядра в NTFS при инициализации этого кода. Функции EFS осуществляют шифрование и расшифровку файловых данных по мере обращения приложений к шифрованным файлам. Хотя EFS хранит FEK вместе с данными файла, FEK шифруется с помощью открытого ключа индивидуального пользователя. Для шифрования или расшифровки файловых данных EFS должна расшифровать FEK файла, обращаясь к криптографическим сервисам пользовательского режима.

Подсистема локальной аутентификации (Local Security Authentication Subsystem, LSASS) (\Windows\System32\Lsass.exe) не только управляет сеансами регистрации, но и выполняет все рутинные операции, связанные с управлением ключами EFS. Например, когда драйверу EFS требуется расшифровать FEK для расшифровки данных файла, к которому обращается пользователь, драйвер EFS посылает запрос LSASS через LPC Драйвер устройства KSecDD (\Windows\System32\Drivers\Ksecdd.sys) экспортирует функции, необходимые драйверам для посылки LPC-сообщений LSASS. Компонент LSASS, сервер локальной аутентификации (Local Security Authentication Server, Lsasrv) (\Windows\System32\Lsasrv.dll), ожидает запросы на расшифровку FEK через RPC; расшифровка выполняется соответствующей функцией EFS, которая также находится в Lsasrv. Для расшифровки FEK, передаваемого LSASS драйвером EFS в зашифрованном виде, Lsasrv использует функции Microsoft CryptoAPI (CAPI).

CryptoAPI состоит из DLL провайдеров криптографических сервисов (cryptographic service providers, CSP), которые обеспечивают приложениям доступ к различным криптографическим сервисам (шифрованию, дешифрованию и хэшированию). Например, эти DLL управляют получением открытого и закрытого ключей пользователя, что позволяет Lsasrv не заботиться о деталях защиты ключей и даже об особенностях работы алгоритмов шифрования. EFS опирается на алгоритмы шифрования RSA, предоставляемые провайдером Microsoft Enhanced Cryptographic Provider (\Windows\ System32\Rsaenh.dll). Расшифровав FEK, Lsasrv возвращает его драйверу EFS в ответном LPC-сообщении. Получив расшифрованный FEK, EFS с помощью DESX расшифровывает данные файла для NTFS.

Подробнее о том, как EFS взаимодействует с NTFS и как Lsasrv управляет ключами через CryptoAPI, мы поговорим в следующих разделах.

Первое шифрование файла

Обнаружив шифрованный файл, драйвер NTFS вызывает функции EFS. O состоянии шифрования файла сообщают его атрибуты — так же, как и о состоянии сжатия в случае сжатых файлов. NTFS и EFS имеют специальные интерфейсы для преобразования файла из незашифрованной в зашифрованную форму, но этот процесс протекает в основном под управлением компонентов пользовательского режима. Как уже говорилось, Windows позволяет шифровать файлы двумя способами: утилитой командной строки cipher или установкой флажка Encrypt Contents To Secure Data на вкладке Advanced Attributes окна свойств файла в Windows Explorer. Windows Explorer и cipher используют Windows-функцию EncryptFile, экспортируемую Advapi32.dll (Advanced Windows API DLL). Чтобы получить доступ к API, который нужен для LPC-вызова интерфейсов EFS в Lsasrv, Advapi32 загружает другую DLL, Feclient.dll (File Encryption Client DLL).

Получив RPC-сообщение с запросом на шифрование файла от Feclient, Lsasrv использует механизм олицетворения Windows для подмены собой пользователя, запустившего программу, шифрующую файл (cipher или Windows Explorer). Это заставляет Windows воспринимать файловые операции, выполняемые Lsasrv, как операции, выполняемые пользователем, желающим зашифровать файл. Lsasrv обычно работает под учетной записью System (об этой учетной записи см. главу 8). Если бы Lsasrv не олицетворял пользователя, то не получил бы прав на доступ к шифруемому файлу.

Далее Lsasrv создает файл журнала в каталоге System Volume Information, где регистрирует ход процесса шифрования. Имя файла журнала — обычно EfsO.log, но, если шифруется несколько файлов, O заменяется числом, которое последовательно увеличивается на 1 до тех пор, пока не будет получено уникальное имя журнала для текущего шифруемого файла.

CryptoAPI полагается на информацию пользовательского профиля, хранящуюся в реестре, поэтому, если профиль еще не загружен, следующий шаг Lsasrv — загрузка в реестр профиля олицетворяемого пользователя вызовом функции LoadUserProfiIe из Userenv.dll (User Environment DLL). Обычно профиль пользователя к этому моменту уже загружен, поскольку Winlogon загружает его при входе пользователя в систему. Ho если пользователь регистрируется под другой учетной записью с помощью команды RunAs, то при попытке обращения к зашифрованному файлу под этой учетной записью соответствующий профиль может быть не загружен.

После этого Lsasrv генерирует для файла FEK, обращаясь к средствам шифрования RSA, реализованным в Microsoft Base Cryptographic Provider 1.0.

Создание связок ключей

K этому моменту Lsasrv уже получил FEK и может сгенерировать информацию EFS, сохраняемую вместе с файлом, включая зашифрованную версию FEK. Lsasrv считывает из параметра реестра HKCU\Software\Microsoft\Win-dows NT\CurrentVersion\EFS\CurrentKeys\CertificateHash значение, присвоенное пользователю, который затребовал операцию шифрования, и получает сигнатуру открытого ключа этого пользователя. (Заметьте, что этот раздел не появляется в реестре, если ни один файл или каталог не зашифрован.) Lsasrv использует эту сигнатуру для доступа к открытому ключу пользователя и для шифрования FEK.

Теперь Lsasrv может создать информацию, которую EFS сохранит вместе с файлом. EFS хранит в шифрованном файле только один блок информации, в котором содержатся записи для всех пользователей этого файла. Данные записи называются элементами ключей (key entries); они хранятся в области сопоставленных с файлом данных EFS, которая называется Data Decryption Field (DDF). Совокупность нескольких элементов ключей называется связкой ключей (key ring), поскольку, как уже говорилось, EFS позволяет нескольким лицам совместно использовать шифрованный файл.

Формат данных EFS, сопоставленных с файлом, и формат элемента ключа показан на рис. 12–58. B первой части элемента ключа EFS хранит информацию, достаточную для точного описания открытого ключа пользователя. B нее входит SID пользователя (его наличие не гарантируется), имя контейнера, в котором хранится ключ, имя провайдера криптографических сервисов и хэш сертификата криптографической пары (при расшифровке используется только этот хэш). Bo второй части элемента ключа содержится шифрованная версия FEK. Lsasrv шифрует FEK через CryptoAPI по алгоритму RSA с применением открытого ключа данного пользователя.

Далее Lsasrv создает еще одну связку ключей, содержащую элементы ключей восстановления (recovery key entries). EFS хранит информацию об этих элементах в поле DRF файла (см. рис. 12–58). Формат элементов DRF идентичен формату DDE DRF служит для расшифровки пользовательских данных по определенным учетным записям (агентов восстановления) в тех случаях, когда администратору нужен доступ к пользовательским данным. Допустим, сотрудник компании забыл свой пароль для входа в систему. B этом случае администратор может сбросить пароль этого сотрудника, но без агентов восстановления никто не сумеет восстановить его зашифрованные данные.

Агенты восстановления (Recovery Agents) определяются в политике безопасности Encrypted Data Recovery Agents (Агенты восстановления шифрованных данных) на локальном компьютере или в домене. Эта политика доступна через оснастку Group Policy (Групповая политика) консоли MMC Запустите Recovery Agent Wizard (Мастер добавления агента восстановления), щелкнув правой кнопкой мыши строку Encrypted Data Recovery Agents (рис. 12–59) и последовательно выбрав команды New (Создать) и Encrypted Recovery Agent (Агент восстановления шифрованных данных). Вы можете добавить агенты восстановления и указать, какие криптографические пары (обозначенные их сертификатами) могут использовать эти агенты для восстановления шифрованных данных. Lsasrv интерпретирует политику восстановления в процессе своей инициализации или при получении уведомления об изменении политики восстановления. EFS создает DRF-элементы ключей для каждого агента восстановления, используя провайдер криптографических сервисов, зарегистрированный для EFS-восстановления. Провайдером по умолчанию служит Base Cryptographic Provider 1.0.

Ha завершающем этапе создания информации EFS для файла Lsasrv вычисляет контрольную сумму для DDF и DRF по механизму хэширования MD5 из Base Cryptographic Provider 1.0. Lsasrv хранит вычисленную контрольную сумму в заголовке данных EFS. EFS ссылается на эту сумму при расшифровке, чтобы убедиться в том, что сопоставленные с файлом данные EFS не повреждены и не взломаны.

Шифрование файловых данных

Ход процесса шифрования показан на рис. 12–60. После создания всех данных, необходимых для шифруемого пользователем файла, Lsasrv приступает к шифрованию файла и создает его резервную копию, Efs0.tmp (если есть другие резервные копии, Lsasrv просто увеличивает номер в имени резервного файла). Резервная копия помещается в тот каталог, где находится шифруемый файл. Lsasrv применяет к резервной копии ограничивающий дескриптор защиты, так что доступ к этому файлу можно получить только по учетной записи System. Далее Lsasrv инициализирует файл журнала, созданный им на первом этапе процесса шифрования и регистрирует в нем факт создания резервного файла. Lsasrv шифрует исходный файл только после его резервирования.

Наконец, Lsasrv посылает через NTFS коду EFS режима ядра команду на добавление к исходному файлу созданной информации EFS. B Windows 2000 NTFS получает эту команду, но поскольку она не понимает команд EFS, то просто вызывает драйвер EFS. Код EFS режима ядра принимает посланные Lsasrv данные EFS и применяет их к файлу через функции, экспортируемые NTFS. Эти функции позволяют EFS добавлять к NTFS-файлам атрибут $EFS. После этого управление возвращается к Lsasrv, который копирует содержимое шифруемого файла в резервный. Закончив создание резервной копии (в том числе скопировав все дополнительные потоки данных), Lsasrv отмечает в файле журнала, что резервный файл находится в актуальном состоянии. Затем Lsasrv посылает NTFS другую команду, требуя зашифровать содержимое исходного файла.

Получив от EFS команду на шифрование файла, NTFS удаляет содержимое исходного файла и копирует в него данные резервного. По мере копирования каждого раздела файла NTFS сбрасывает данные раздела из кэша файловой системы, и они записываются на диск. Поскольку файл помечен как шифрованный, NTFS вызывает EFS для шифрования раздела данных перед записью на диск. EFS использует незашифрованный FEK, переданный NTFS, чтобы шифровать файловые данные по алгоритму DESX, AES или 3DES порциями, равными по размеру одному сектору (512 байтов).

После того как файл зашифрован, Lsasrv регистрирует в файле журнала, что шифрование успешно завершено, и удаляет резервную копию файла. B заключение Lsasrv удаляет файл журнала и возвращает управление приложению, запросившему шифрование файла.

Сводная схема процесса шифрования

Ниже приведен сводный список этапов шифрования файла через EFS.

1. Загружается профиль пользователя, если это необходимо.

2. B каталоге System Volume Information создается файл журнала с именем Efsxlog, где x — уникальное целое число от 0. По мере выполнения следующих этапов в журнал заносятся записи, позволяющие восстановить файл после сбоя системы в процессе шифрования.

3. Base Cryptographic Provider 1.0 генерирует для файла случайное 128-битное число, используемое в качестве FEK.

4. Генерируется или считывается криптографическая пара ключей пользователя. Она идентифицируется в HKCU\Software\Microsoft\Windows NT\ CurrentVersion\EFS\CurrentKeys\CertificateHash.

5. Для файла создается связка ключей DDF с элементом для данного пользователя. Этот элемент содержит копию FEK, зашифрованную с помощью открытого EFS-ключа пользователя.

6. Для файла создается связка ключей DRF B нем есть элементы для каждого агента восстановления в системе, и при этом в каждом элементе содержится копия FEK, зашифрованная с помощью открытого EFS-ключа агента.

7. Создается резервный файл с именем вида Efs0.tmp в том каталоге, где находится и шифруемый файл.

8. Связки ключей DDF и DRF добавляются к заголовку и сопоставляются с файлом как атрибут EFS.

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

10. Содержимое исходного файла уничтожается, в него копируется содержимое резервного. B результате этой операции данные исходного файла шифруются, так как теперь файл помечен как шифрованный.

11. Удаляется резервный файл.

12. Удаляется файл журнала.

13. Выгружается профиль пользователя (загруженный на этапе 1).

При сбое системы во время шифрования согласованные данные непременно сохранятся в одном из файлов — исходном или резервном. Когда Lsasrv инициализируется после сбоя системы, он ищет файлы журнала в каталоге System Volume Information на каждом NTFS-томе в системе. Если Lsasrv находит один или несколько файлов журнала, он изучает их содержимое и определяет порядок восстановления. Если исходный файл не был модифицирован на момент аварии, Lsasrv удаляет файл журнала и соответствующий резервный файл; в ином случае он копирует резервный файл поверх исходного (частично шифрованного) файла, после чего удаляет журнал и резервную копию. После того как Lsasrv обработает файлы журналов, файловая система возвращается в целостное состояние без потери пользовательских данных.

Процесс расшифровки

Процесс расшифровки начинается, когда пользователь открывает шифрованный файл. При открытии файла NTFS анализирует его атрибуты и выполняет функцию обратного вызова в драйвере EFS. Драйвер EFS считывает атрибут $EFS, сопоставленный с шифрованным файлом. Чтобы прочитать этот атрибут, драйвер вызывает функции поддержки EFS, которые NTFS экспортирует для EFS. NTFS выполняет все необходимые действия, чтобы открыть файл. Драйвер EFS проверяет наличие у пользователя, открывающего файл, прав доступа к данным шифрованного файла (т. е. зашифрованный FEK в связке ключей DDF или DRF должен соответствовать криптографической паре ключей, сопоставленной с пользователем). После такой проверки EFS получает расшифрованный FEK файла, применяемый для обработки данных в операциях, которые пользователь может выполнять над файлом.

EFS не может расшифровать FEK самостоятельно и полагается в этом на Lsasrv (который может использовать CryptoAPI). C помощью драйвера Ksecdd.sys EFS посылает LPC-сообщение Lsasrv, чтобы тот извлек из атрибута IEFS (т. е. из данных EFS) FEK пользователя, открывающего файл, и расшифровал его.

Получив LPC-сообщение, Lsasrv вызывает функцию LoadUserProfile из Use-renv.dll для загрузки в реестр профиля пользователя, если он еще не загружен. Lsasrv перебирает все поля ключей в данных EFS, пробуя расшифровать каждый FEK на основе закрытого ключа пользователя; с этой целью Lsasrv пытается расшифровать FEK в DDF- или DRF-элементе ключа. Если хэш сертификата в поле ключа не подходит к ключу пользователя, Lsasrv переходит к следующему полю ключа. Если Lsasrv не удастся расшифровать ни одного FEK в DDF или DRF, пользователь не получит FEK файла, и EFS запретит доступ к файлу приложению, которое пыталось открыть этот файл. A если Lsasrv найдет какой-нибудь хэш, который соответствует ключу пользователя, он расшифрует FEK по закрытому ключу пользователя через CryptoAPI.

Lsasrv, обрабатывая при расшифровке FEK связки ключей DDF и DRF, автоматически выполняет операции восстановления файла. Если к файлу пытается получить доступ агент восстановления, не зарегистрированный на доступ к шифрованному файлу (т. е. у него нет соответствующего поля в связке ключей DDF), EFS позволит ему обратиться к файлу, потому что агент имеет доступ к паре ключей для поля ключа в связке ключей DRE

Кэширование расшифрованного FEK

Путь от драйвера EFS до Lsasrv и обратно требует довольно много времени — в процессе расшифровки FEK в типичной системе CryptoAPI использует результаты более 2000 вызовов API-функций реестра и 400 обращений к файловой системе. Чтобы сократить издержки от всех этих вызовов, драйвер EFS использует кэш в паре с NTFS.

Расшифровка файловых данных

Открыв шифрованный файл, приложение может читать и записывать его данные. Для расшифровки файловых данных NTFS вызывает драйвер EFS по мере чтения этих данных с диска — до того, как помещает их в кэш файловой системы. Аналогичным образом, когда приложение записывает данные в файл, они остаются незашифрованными в кэше файловой системы, пока приложение или диспетчер кэша не сбросит данные обратно на диск с помощью NTFS. При записи данных шифрованного файла из кэша на диск NTFS вызывает драйвер EFS, чтобы зашифровать их.

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

Резервное копирование шифрованных файлов

Важный аспект разработки любого механизма шифрования файлов заключается в том, что приложения не могут получить доступ к расшифрованным данным иначе, чем через механизмы шифрования. Это ограничение особенно важно для утилит резервного копирования, с помощью которых файлы сохраняются на архивных носителях. EFS решает эту проблему, предоставляя утилитам резервного копирования механизм, с помощью которого они могут создавать резервные копии файлов и восстанавливать их в шифрованном виде. Таким образом, утилитам резервного копирования не обязательно шифровать или расшифровывать данные файлов в процессе резервного копирования.

Для доступа к шифрованному содержимому файлов утилиты резервного копирования в Windows используют новый EFS APL функции OpenEncrypted-FileRatv, ReadEncryptedFileRaw, WriteEncryptedFileRaw и CloseEncryptedFileRaiv. Эти функции, предоставляемые Advapi32.dll, вызывают соответствующие функции Lsasrv по механизму LPC Например, после того как утилита резервного копирования открывает файл, она vbi3biw‹kCTReadEncryptedFileRaiv, чтобы получить данные. Lsasrv-функция EfsReadFileRaw выдает управляющие команды (шифруемые по алгоритму DESX, AES или 3DES с помощью сеансового ключа EFS) драйверу NTFS для чтения сначала атрибута EFS файла, а затем его шифрованного содержимого.

EfsReadFileRaw может понадобиться несколько операций чтения, чтобы считать большой файл. По мере того как EfsReadFileRaw считывает очередную порцию файла, Lsasrv посылает Advapi32.dll RPC-сообщение, в результате которого выполняется функция обратного вызова, указанная программой резервного копирования при вызове ReadEncryptedFileRaw. Функция EfsReadFileRaiv передает считанные шифрованные данные функции обратного вызова, которая записывает их на архивный носитель. Восстанавливаются шифрованные файлы аналогичным образом. Программа резервного копирования вызывает API-функцию WriteEncryptedFileRaw, которая активизирует функцию обратного вызова программы резервного копирования для получения нешифрованных данных с архивного носителя, в то время как Lsasrv-функция EfsWriteFileRaw восстанавливает содержимое файла.

ЭКСПЕРИМЕНТ: просмотр информации EFS

EFS поддерживает массу других API-функций, с помощью которых приложения могут манипулировать шифрованными файлами. Так, функция AddUsersToEncryptedFile позволяет предоставлять доступ к шифрованному файлу дополнительным пользователям, а функция RemoveUsersFromEncryptedFile — запрещать доступ к нему указанным пользователям. Функция QueryUsersOnEncryptedFile сообщает о сопоставленных с файлом полях ключей DDF и DRE Она возвращает SID, хэш сертификата и содержимое каждого поля ключа DDF и DRE Ниже приведен образец вывода утилиты EFSDump (www.sysinternals.com).

B качестве параметра ее командной строки указано имя шифрованного файла.

Как видите, в файле test.txt имеется один элемент DDF, соответствующий пользователю Mark, и один элемент DRF, соответствующий Administrator, который является единственным агентом восстановления, зарегистрированным в системе на данный момент.

Чтобы убедиться в наличии атрибута $EFS в зашифрованном файле, используйте утилиту Nfi из OEM Support Tools:

Резюме

Windows поддерживает широкий спектр форматов файловых систем, доступных как локальной системе, так и удаленным клиентам. Архитектура драйвера фильтра файловой системы позволяет корректно расширять и дополнять средства доступа к файловой системе, a NTFS является надежным, безопасным и масштабируемым форматом файловой системы. B следующей главе мы рассмотрим поддержку сетей в Windows.

 

ГЛABA 13 Поддержка сетей

Windows создавалась с учетом необходимости работы в сети, поэтому в операционную систему включена всесторонняя поддержка сетей, интегрированная с подсистемой ввода-вывода и Windows API. K четырем базовым типам сетевого программного обеспечения относятся сервисы, API, протоколы и драйверы устройств сетевых адаптеров. Все они располагаются один над другим, образуя сетевой стек. Для каждого уровня в Windows предусмотрены четко определенные интерфейсы, поэтому в дополнение к большому набору API-функций, протоколов и драйверов адаптеров, поставляемых с Windows, сторонние разработчики могут создавать собственные компоненты, расширяющие сетевую функциональность операционной системы.

B этой главе будет рассмотрен весь сетевой стек Windows — снизу доверху. Сначала мы поговорим о том, как сетевые компоненты Windows соотносятся с уровнями эталонной модели OSI (Open Systems Interconnection). Далее мы кратко опишем сетевые API, доступные в Windows, и покажем, как они реализованы. Вы узнаете, что делают редиректоры, как происходит разрешение имен сетевых ресурсов и как устроены драйверы протоколов. Познакомившись с реализацией драйверов устройств сетевых адаптеров, мы расскажем о привязке, в ходе которой сервисы и стеки протоколов связываются с сетевыми адаптерами.

Сетевая архитектура Windows

Задача сетевого программного обеспечения состоит в приеме запроса (обычно на ввод-вывод) от приложения на одной машине, передаче его на другую, выполнении запроса на удаленной машине и возврате результата на первую машину. B ходе этих операций запрос неоднократно трансформируется. Высокоуровневый запрос вроде «считать x байтов из файла у на машине z» требует, чтобы программное обеспечение определило, как достичь машины z и какой коммуникационный протокол она понимает. Затем запрос должен быть преобразован для передачи по сети — например, разбит на короткие пакеты данных. Когда запрос достигнет другой стороны, нужно проверить его целостность, декодировать и послать соответствующему компоненту операционной системы. По окончании обработки запрос должен быть закодирован для обратной передачи по сети.

Эталонная модель OSI

Чтобы помочь поставщикам в стандартизации и интеграции их сетевого программного обеспечения, международная организация по стандартизации (ISO) определила программную модель пересылки сообщений между компьютерами. Эта модель получила название эталонной модели OSI (Open Systems Interconnection). B ней определено семь уровней программного обеспечения (рис. 13-1).

Эталонная модель OSI — идеал, точно реализованный лишь в очень немногих системах, но часто используемый при объяснении основных принципов работы сети. Каждый уровень на одной из машин считает, что он взаимодействует с тем же уровнем на другой машине. Ha данном уровне обе машины «разговаривают» на одном языке, или протоколе. Ho в действительности сетевой запрос должен сначала пройти до самого нижнего уровня на первой машине, затем он передается по несущей среде и уже на второй машине вновь поднимается до уровня, который его поймет и обработает.

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

• Прикладной уровень (application layer) Обрабатывает передачу данных между двумя сетевыми приложениями, включая проверку прав доступа, идентификацию взаимодействующих машин и инициацию обмена данными.

• Презентационный уровень (presentation layer) Отвечает за форматирование данных, в том числе решает, должны ли строки заканчиваться парой символов «возврат каретки/перевод строки» (CR/LF) или только символом «возврат каретки» (CR), надо ли сжимать данные, кодировать и т. д.

• Сеансовый уровень (session layer) Управляет соединением взаимодействующих приложений, включая высокоуровневую синхронизацию и контроль за тем, какое из них «говорит», а какое «слушает».

• Транспортный уровень (transport layer) Ha передающей стороне разбивает сообщения на пакеты и присваивает им порядковые номера, гарантирующие прием пакетов в должном порядке. Кроме того, изолирует сеансовый уровень от влияния изменений в составе оборудования.

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

• Канальный уровень (data-link layer) Пересылает низкоуровневые кадры данных, ждет подтверждений об их приеме и повторяет передачу кадров, потерянных в ненадежных линиях связи.

• Физический уровень (physical layer) Передает биты по сетевому кабелю или другой физической несущей среде.

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

Сетевые компоненты Windows

Ha рис. 13-2 представлена общая схема сетевых компонентов Windows, их соответствие уровням модели OSI, а также протоколы, используемые различными уровнями. Как видите, между уровнями OSI и реальными сетевыми компонентами нет точного соответствия. Некоторые компоненты охватывают несколько уровней. Ниже приводится список сетевых компонентов с кратким описанием.

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

• Клиенты TDI (Transport Driver Interface) Драйверы устройств режима ядра, обычно реализующие ту часть сетевого API, которая работает в режиме ядра. Клиенты TDI называются так из-за того, что пакеты запросов ввода-вывода (IRP), которые они посылают драйверам протоколов, форматируются по стандарту Transport Driver Interface (документированному в DDK). Этот стандарт определяет общий интерфейс программирования драйверов устройств режима ядра. (Об IRP см. главу 9)

• Транспорты TDI Представляют собой драйверы протоколов режима ядра и часто называются транспортами, NDlS-драйверами протоколов или драйверами протоколов. Они принимают IRP от клиентов TDI и обрабатывают запросы, представленные этими IRP Обработка запросов может потребовать взаимодействия через сеть с другим равноправным компьютером; в таком случае транспорт TDI добавляет к данным IRP заголовки, специфичные для конкретного протокола (TCP, UDP, IPX), и взаимодействует с драйверами адаптеров через функции NDIS (также документированные в DDK). B общем, транспорты TDI связывают приложения через сеть, выполняя такие операции, как сегментация сообщений, их восстановление, упорядочение, подтверждение и повторная передача.

• Библиотека NDIS (Ndis.sys) Инкапсулирует функциональность для драйверов адаптеров, скрывая от них специфику среды Windows, работающей в режиме ядра. Библиотека NDIS экспортирует функции для транспортов TDI, а также функции поддержки для драйверов адаптеров.

Рис. 13-2. Модель OSI и сетевые компоненты Windows

• Минипорт-драйверы NDIS Драйверы режима ядра, отвечающие за организацию интерфейсов между транспортами TDI и конкретными сетевыми адаптерами. Минипорт-драйверы NDIS пишутся так, чтобы они были заключены в оболочку библиотеки NDIS. Такая инкапсуляция обеспечивает межплатформенную совместимость с потребительскими версиями Microsoft Windows. Минипорт-драйверы NDIS не обрабатывают IRP, а регистрируют интерфейс таблицы вызовов библиотеки NDIS, которая содержит указатели на функции, соответствующие функциям, экспортируемым библиотекой NDIS для транспортов TDI. Минипорт-драйверы NDIS взаимодействуют с сетевыми адаптерами, используя функции библиотеки NDIS, которые вызывают соответствующие функции HAL. Фактически четыре нижних сетевых уровня часто обозначают собирательным термином «транспорт», а компоненты, расположенные на трех верхних уровнях, — термином «пользователи транспорта».

Далее мы подробно рассмотрим сетевые компоненты, показанные на рис. 13-2 (равно как и не показанные на нем), обсудим их взаимосвязи и то место, которое они занимают в Windows.

Сетевые API

Для поддержки унаследованных приложений и для совместимости с промышленными стандартами в Windows реализован целый набор сетевых API. B этом разделе мы расскажем о сетевых API и поясним, как они используются приложениями. Важно иметь в виду, что выбор API для приложения определяется характеристиками APL поверх каких протоколов он может работать, поддерживает ли он надежную и двустороннюю связь, а также переносим ли он на другие Windows-платформы, на которых может работать данное приложение. Мы обсудим следующие сетевые APL

• Windows Sockets (Winsock);

• Remote Procedure Call (RPC);

• API доступа к Web;

• именованные каналы (named pipes) и почтовые ящики (mailslots);

• NetBIOS:

• прочие сетевые API.

Windows Sockets

Изначально Windows Sockets (Winsock) версии 1.0 был Microsoft-реализацией BSD (Berkeley Software Distribution) Sockets, программного интерфейса, с 80-х годов прошлого века ставшего стандартом, на основе которого UNIX-системы взаимодействовали через Интернет. Поддержка сокетов в Windows существенно упрощает перенос сетевых приложений из UNIX в Windows. Современные версии Winsock включают большую часть функциональности BSD Sockets, а также содержат специфические расширения от Microsoft, развитие которых продолжается. Winsock поддерживает как надежные коммуникации, ориентированные на логические соединения, так и ненадежные коммуникации, не требующие логических соединений. Windows предоставляет Winsock 2.2 — для устаревших версий Windows он доступен в виде надстройки. Функциональность Winsock 2.2 выходит далеко за рамки спецификации BSD Sockets, и, в частности, он поддерживает функции, использующие средства асинхронного ввода-вывода в Windows, что обеспечивает гораздо более высокую производительность и масштабируемость, чем исходный BSD Sockets.

Winsock обеспечивает:

• ввод-вывод по механизму «scatter/gather» и асинхронный ввод-вывод;

• поддержку Quality of Service (QoS) — если нижележащая сеть поддерживает QoS, приложения могут согласовывать между собой максимальные задержки и полосы пропускания;

• расширяемость — Winsock можно использовать не только с протоколами, которые он поддерживает в Windows, но и с другими;

• поддержку интегрированных пространств имен, отличных от определенных протоколом, который используется приложением вместе с Winsock. Например, сервер может опубликовать свое имя в Active Directory, а клиент, используя расширения пространств имен, — найти адрес сервера в Active Directory;

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

Далее мы рассмотрим принципы работы Winsock и опишем способы его расширения.

Функционирование Winsock на клиентской стороне

Первый шаг Winsock-приложения — инициализация Winsock API вызовом инициализирующей функции. B Windows 2000 такое приложение должно затем создать сокет, представляющий конечную точку коммуникационного соединения. Приложение получает адрес сервера, к которому ему нужно подключиться, вызовом gethostbyname. Winsock не зависит от конкретного протокола, поэтому адрес может быть указан по любому установленному в системе протоколу, поверх которого работает Winsock (TCP/IP, TCP/IP с IP версии 6, IPX). Получив адрес сервера, клиент, ориентированный на логические соединения (connection-oriented client), пытается подключиться к этому серверу, вызывая функцию connect и передавая ей адрес сервера.

B Windows XP и Windows Server 2003 приложение должно получить адрес сервера через getaddrinfo, а не gethostbyname. Функция getaddrinfo возвращает список адресов, назначенных серверу, и клиент пытается поочередно подключиться по каждому из них до тех пор, пока ему не удастся установить соединение. Это гарантирует, что клиент, поддерживающий, например, только IP версии 4 (IPv4), соединится с сервером, которому могли быть назначены как IPv4-, так и IPv6-адpeca, по соответствующему IPv4-адpecy.

Установив соединение, клиент может посылать и принимать данные через свой сокет, используя, например, recv и send. Клиент, не ориентированный на логические соединения (connectionless client), указывает удаленный адрес через эквивалентные функции API, не ориентированного на логические соединения; в данном случае — через sendto и recvfrom соответственно.

Функционирование Winsock на серверной стороне

Последовательность операций серверного приложения отличается от таковой для клиентского. После инициализации Winsock API сервер создает сокет и выполняет его привязку к локальному адресу через bind. Как и в случае клиентского приложения, тип адреса (по TCP/IP, TCP/IP с IP версии 6 или какому-то другому протоколу) выбирается серверным приложением.

Если сервер ориентирован на логические соединения, он выполняет на сокете операцию listen, указывая число соединений, которое он может поддерживать на этом сокете. Далее он выполняет операцию accept, чтобы клиент мог подключиться к сокету. При наличии ждущего запроса на соединение вызов accept завершается немедленно. B ином случае он завершается лишь после поступления запроса на соединение. После того как соединение установлено, функция accept возвращает новый сокет, представляющий серверную сторону соединения. Сервер может выполнять операции приема и передачи данных с помощью таких функций, как, например, recv и send. Рис. 13-3 иллюстрирует коммуникационную связь между клиентом и сервером Winsock, ориентированными на логические соединения.

После привязки к адресу сервер, не требующий логических соединений, ничем не отличается от аналогичного клиента: он посылает и получает данные через сокет, просто указывая удаленный адрес для каждой операции. Большинство протоколов, не ориентированных на логические соединения, ненадежны и, как правило, не позволяют определить, получил ли адресат отправленные ему пакеты данных — дейтаграммы (datagrams). Такие протоколы идеальны для передачи коротких сообщений, когда надежность доставки не играет определяющей роли (впрочем, приложение может само реализовать соответствующие средства поверх протокола).

Расширения Winsock

C точки зрения программирования для Windows, сильной стороной Winsock API является его интеграция с механизмом Windows-сообщений. Winsock-приложение может использовать преимущества такой интеграции для выполнения асинхронных операций с сокетом и приема уведомления о завершении операции через стандартное Windows-сообщение или функцию обратного вызова. Это упрощает разработку Windows-приложений, поскольку позволяет отказаться от многопоточности и синхронизирующих объектов для поддержки сетевого ввода-вывода и реакции на пользовательский ввод или запросы диспетчера окон на обновление окон приложения.

Кроме вспомогательных функций, прямо соответствующих функциям, реализованным в BSD Sockets, Microsoft добавила несколько функций, не входящих в стандарт Winsock. Две из них, AcceptEx и TransmitFile, стоят того, чтобы привести здесь их описание, так как благодаря им многие Web-серверы под управлением Windows достигают высокой производительности. AcceptEx является версией функции accept, которая в процессе установления соединения с клиентом возвращает адрес и первое сообщение клиента. AcceptEx дает возможность серверному приложению подготовиться к серии операций accept для последующей обработки множества входящих соединений. A это позволяет Web-серверу избежать выполнения сразу нескольких Winsock-функций.

Установив соединение с клиентом, Web-сервер обычно посылает ему файл, например Web-страницу. Реализация функции TransmitFile интегрирована с диспетчером кэша, что позволяет серверу посылать файл непосредственно из кэша файловой системы. Такая пересылка данных называется нулевым копированием (zero-сору), поскольку в этом случае серверу не приходится обращаться к файловым данным: он просто указывает описатель файла и диапазон пересылаемых байтов. Кроме того, функция TransmitFile позволяет серверу присоединять к началу или концу файла дополнительные данные. Это дает ему возможность посылать заголовочную информацию, например имя Web-сервера и поле, в котором указывается размер посылаемого сообщения. Internet Information Services (IIS), входящая в комплект поставки Windows, использует как AcceptEx, так и TransmitFile.

B Windows XP и Windows Server 2003 добавлен целый набор других, многофункциональных API-функций, в том числе ConnectEx, DisconnectEx и TransmitPackets. ConnectEx устанавливает соединение и посылает первое сообщение по этому соединению. DisconnectEx закрывает соединение и разрешает повторное использование описателя сокета, представляющего данное соединение, в вызове AcceptEx или ConnectEx. Наконец, TransmitPackets — полный аналог TransmitFile с тем исключением, что позволяет передавать не только файловые данные, но и данные, находящиеся в памяти.

Принципы расширения Winsock

Winsock является расширяемым API, поскольку сторонние разработчики могут добавлять провайдеры транспортных сервисов (transport service providers, TSP), организующие интерфейсы Winsock и другими протоколами или уровнями поверх существующих протоколов (это позволяет реализовать такую функциональность, как поддержка прокси). Сторонние разработчики также могут добавлять провайдеры пространств имен (namespace service providers), дополняющие механизмы разрешения имен в Winsock. Такие компоненты подключаются к Winsock через его интерфейс провайдеров сервисов (service provider interface, SPI). Если какой-то TSP регистрируется в Winsock, последний реализует на его основе функции сокета (вроде connect и accepf) для тех типов адресов, которые указаны этим провайдером как поддерживаемые. Никаких ограничений на то, как TSP реализует функции, не налагается, но такая реализация обычно требует взаимодействия с драйвером транспорта в режиме ядра.

Требование к любому клиент-серверному приложению, использующему Winsock, заключается в следующем: сервер должен сделать свой адрес доступным клиентам, чтобы они могли подключаться к серверу. Для стандартных сервисов, выполняемых в TCP/IP, с этой целью используются так называемые общеизвестные адреса. Если браузер знает имя компьютера, на котором работает Web-сервер, он может подключиться к нему, указав общеизвестный адрес Web-сервера (к IP-адресу сервера добавляется строка «:80» — номер HTTP-порта). Провайдеры пространств имен позволяют серверам регистрировать свое присутствие и другими способами. Например, провайдер пространства имен мог бы на серверной стороне регистрировать адрес сервера в Active Directory, а на клиентской — искать его в Active Directory. Провайдеры пространств имен обеспечивают эту функциональность Winsock, реализуя такие стандартные Winsock-функции разрешения имен, как getaddrinfo (заменяет gethostbyname) и getnameinfo.

ЭКСПЕРИМЕНТ: просмотр провайдеров сервисов Winsock

Утилита Windows Sockets Configuration (Sporder.exe), входящая в Platform SDK, показывает зарегистрированные в Winsock провайдеры транспортных сервисов и пространств имен и позволяет изменять порядок перечисления TSP Например, если в системе имеется два провайдера транспортных сервисов TCP/IP, то первым в списке идет TSP по умолчанию для Winsock-приложений, использующих протокол TCP/IP. Ha иллюстрации показано окно Sporder, в котором перечислены зарегистрированные TSP

Реализация Winsock

Реализация Winsock представлена на рис. 13-4. Его программный интерфейс поддерживается библиотекой Ws2_32.dll (\Windows\System32\Ws2_32.dll), которая обеспечивает приложениям доступ к функциям Winsock. Для операций над именами и сообщениями Ws2_32.dll вызывает сервисы TSP и провайдеров пространств имен. Библиотека Mswsock.dll выступает в роли TSP для протоколов, поддерживаемых Microsoft в Winsock. Она взаимодействует с драйверами протоколов режима ядра с помощью вспомогательных библиотек Winsock (Winsock Helpers), специфичных для конкретного протокола. Например, Wshtcpip.dll — вспомогательная библиотека TCP/IP. B Mswsock.dll (\Windows\System32\Mswsock.dll) реализованы такие расширения Winsock, как функции TransmitEile,AcceptEx и WSARecvEx. Windows поставляется со вспомогательными библиотеками для TCP/IP, TCP/IP с IPv6, AppleTalk, IPX/SPX, ATM и IrDA (Infrared Data Association), а также с провайдерами пространств имен для DNS (TCP/IP), Active Directory и IPX/SPX.

Рис. 13-4. Реализация Winsock

Подобно API именованных каналов и почтовых ящиков Winsock интегрируется с Windows-моделью ввода-вывода и использует для представления co-кетов описатели файлов. Для этого нужна помощь со стороны драйвера файловой системы режима ядра, поэтому, реализуя функции на основе сокетов, Msafd.dll использует сервисы AFD (Ancillary Function Driver) (\Windows\System32\Drivers\Afd.sys). AFD является клиентом TDI и выполняет сетевые операции с использованием сокетов, например посылает и принимает сообщения, отправляя TDI IRP-пакеты драйверам протокола. AFD не запрограммирован на использование определенных драйверов протоколов — вместо этого Msafd.dll уведомляет AFD о протоколе, используемом для сокета, и в результате AFD может открыть объект «устройство», представляющий этот протокол.

Windows Sockets Direct

Windows Sockets Direct (WSD) — это интерфейс, позволяющий в Winsock-приложениях без всякой модификации использовать преимущества сетей устройств хранения данных (System Area Networks, SAN). Высокопроизводительные SAN идеальны для самых разнообразных применений — от распределенных вычислений до трехуровневых архитектур электронной коммерции вроде показанной на рис. 13-5. B данной системе сети SAN соединяют Web-серверы (презентационный Web-уровень) с серверами бизнес-логики и с серверами базы данных, что обеспечивает высокоскоростную передачу данных между различными уровнями обработки информации. Поддержка WSD имеется в Windows 2003 и Windows 2000 Data Center Server, а также в Windows 2000 Advanced Server c Service Pack (SP) 2 и выше.

SAN-соединения

Высокая производительность SAN обычно достигается за счет специализированных сетевых соединений и коммутационного оборудования. K наиболее распространенным типам SAN-соединений относятся InfiniBand, Gigabit Ethernet, FiberChannel и различные фирменные (закрытые) решения. Физическая память, разделяемая двумя компьютерами, тоже может служить SAN-соединением.

Коммутационное оборудование SAN реализует немаршрутизируемый протокол, предоставляющий TCP-эквивалентные гарантии, в частности надежную доставку сообщений в правильном порядке. Эти аппаратные средства также поддерживают механизм SAN, называемый удаленным прямым доступом к памяти (Remote Direct Memory Access, RDMA); этот механизм позволяет напрямую передавать сообщения из физической памяти компьютера-источника в физическую память компьютера-получателя без промежуточной операции копирования, которая обычно выполняется на стороне, принимающей сообщения. Благодаря этому RDMA освобождает процессор и шину памяти от лишней нагрузки, связанной с операцией копирования.

Реализации SAN также позволяют обходиться без обращений к компонентам режима ядра, посылая и принимая данные напрямую между пользовательскими приложениями. Это сокращает число системных вызовов, инициируемых приложениями, и соответственно уменьшает время, затрачиваемое на выполнение системного кода поддержки сетей.

Архитектура WSD

Большинство реализаций SAN требуют модификации приложений для взаимодействия с сетевыми протоколами SAN и использования преимуществ аппаратно-реализованных протоколов и механизмов SAN вроде RDMA, но WSD позволяет любому Winsock-приложению, работающему по протоколу TCP, задействовать возможности SAN без такой модификации. Само название WSD подчеркивает, что он обеспечивает приложениям прямой доступ к оборудованию SAN, в обход стека TCP/IP. A сокращение пути передачи данных повышает производительность приложений в 2–2,5 раза.

Такое сокращение достигается за счет использования программного коммутатора, размещаемого на уровень ниже Winsock DLL, как показано на рис. 13-6. Этот коммутатор переадресует сетевые операции SAN провайдеру сервисов Winsock (Winsock service provider, WSP), который предоставляется производителем SAN. WSP служит эквивалентом NDIS-драйвера, работающим в пользовательском режиме, и может проецировать аппаратные регистры SAN на память пользовательского режима, а затем манипулировать оборудованием без участия компонентов режима ядра. Однако некоторые операции все же требуют поддержки со стороны таких компонентов, например для проецирования содержимого аппаратных регистров на память пользовательского режима; эта поддержка тоже предоставляется производителем оборудования SAN. Наконец, производитель SAN предоставляет минипорт-драйвер NDIS, выступающий в роли интерфейса между стеком TCP/IP и оборудованием SAN для приложений, которые используют сетевые средства Winsock, не поддерживаемых SAN на аппаратном уровне.

Remote Procedure CaII (RPC)

RPC — стандарт сетевого программирования, разработанный в начале 80-x. Организация Open Software Foundation (теперь — The Open Group) сделала RPC частью стандарта OSF DCE (Distributed Computing Environment). Несмотря на наличие второго стандарта RPC, SunRPC, реализация RPC от Microsoft совместима со стандартом OSF DCE. RPC, опираясь на другие сетевые API (именованные каналы или Winsock), предоставляет альтернативную модель программирования, в какой-то мере скрывающую детали сетевого программирования от разработчика приложений.

Функционирование RPC

Механизм RPC позволяет создавать приложения, состоящие из произвольного числа процедур, часть которых выполняется локально, а часть — на удаленных компьютерах (через сеть). RPC предоставляет модель работы с сетью, ориентированную на процедуры, а не на транспорты, что упрощает разработку распределенных приложений.

Сетевое программное обеспечение традиционно базируется на модели обработки ввода-вывода. B Windows, например, сетевая операция начинается с того, что приложение выдает запрос на удаленный ввод-вывод. Операционная система обрабатывает запрос, передавая его редиректору, который действует в качестве удаленной файловой системы, обеспечивая прозрачное взаимодействие клиента с удаленной файловой системой. Редиректор передает запрос удаленной файловой системе, а после того как удаленная система выполнит запрос и вернет результаты, локальная сетевая плата генерирует прерывание. Ядро обрабатывает это прерывание, и исходная операция ввода-вывода завершается, возвращая результаты вызывающей программе.

RPC использует совершенно другой подход. Приложения RPC похожи на другие структурированные приложения: у них есть основная программа, которая для выполнения специфических задач вызывает процедуры или библиотеки процедур. Отличие приложений RPC от обычных программ в том, что некоторые библиотеки процедур в приложениях RPC выполняются на удаленных компьютерах, а некоторые — на локальном (рис. 13-7).

Для приложения RPC все процедуры кажутся локальными. Иначе говоря, вместо того чтобы заставлять программиста писать код для передачи запросов на вычисления или ввод-вывод по сети, работы с сетевыми протоколами, обработки сетевых ошибок, ожидания результатов и т. д., программное обеспечение RPC выполняет все эти задачи автоматически. Кроме того, механизм RPC в Windows работает с любыми транспортами, которые имеются в системе.

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

Функционирует приложение RPC следующим образом. B процессе своей работы оно вызывает как локальные процедуры, так и процедуры, отсутствующие на локальной машине. Для обработки последнего случая приложение связывается с локальной DLL, которая содержит интерфейсные процедуры (stub procedures) для всех удаленных процедур. B простой программе интерфейсные процедуры статически связываются с приложением, но в компоненте большего размера они включаются в отдельные DLL. B DCOM обычно применяется последний метод. Интерфейсная процедура имеет то же имя и тот же интерфейс, что и удаленная процедура, но вместо выполнения соответствующей операции она просто преобразует переданные ей параметры для передачи по сети — такой процесс называется маршалингом (marshaling). Маршалинг заключается в упорядочении параметров и их упаковке в определенном формате.

Далее интерфейсная процедура вызывает процедуры библиотеки RPC периода выполнения, и они находят компьютер, на котором расположены удаленные процедуры, определяют используемые этим компьютером механизмы транспорта и посылают запрос при помощи локального программного обеспечения сетевого транспорта. Когда удаленный сервер получает запрос RPC, он выполняет обратное преобразование параметров (unmarshaling), реконструирует исходный вызов процедуры и вызывает ее. Закончив обработку, сервер выполняет обратную последовательность действий для возврата результатов вызывающей программе.

Кроме интерфейса, основанного на описанном здесь синхронном вызове процедур, RPC в Windows также поддерживает асинхронный RPC (asynchronous RPC). Он позволяет приложению RPC вызывать функцию и, не дожидаясь ее выполнения, продолжать свою работу. Ha это время приложение может перейти к выполнению другого кода. Когда от сервера придет ответ, библиотека RPC периода выполнения уведомит клиент о завершении операции. При этом используется механизм уведомления, запрошенный клиентом. Если клиент выбрал для уведомления синхронизирующий объект «событие», он ждет его перехода в свободное состояние, вызвав функцию WaitForSingle-Object или WaitForMultipleObject. Если клиент предоставляет APC (Asynchronous Procedure Call), библиотека RPC периода выполнения ставит APC в очередь потока, выполняющего RPC-функцию. Если же клиент использует в качестве механизма уведомления порт завершения ввода-вывода, он должен вызвать GetQueuedCompletionStatus, чтобы узнать об окончании работы этой функции. Наконец, клиент может опрашивать библиотеку RPC периода выполнения о ходе выполнения операции, вызывая RcpAsyncGetCallStatus.

Помимо библиотеки периода выполнения в Microsoft RPC входит компилятор MIDL (Microsoft Interface Definition Language). Этот компилятор упрощает создание приложений RPC Программист пишет набор обычных прототипов функций (предполагается, что он использует язык C или C++), описывающих удаленные процедуры, а затем помещает их в какой-либо файл. Далее он добавляет к этим прототипам нужную дополнительную информацию, например уникальный для сети идентификатор пакета процедур, номер версии и атрибуты, указывающие, являются ли параметры входными, выходными или и теми, и другими одновременно. B конечном счете программист получает файл на языке IDL (Interface Definition Language).

Подготовленный IDL-файл транслируется компилятором MIDL, который создает интерфейсные процедуры для клиентской и серверной сторон, а также заголовочные файлы, включаемые в приложение. Когда клиентское приложение связывается с файлом интерфейсных процедур, компоновщик разрешает все ссылки на удаленные процедуры. Аналогичным образом удаленные процедуры устанавливаются на серверной машине. Программист, который намерен вызывать существующее приложение RPC, должен написать только клиентскую часть программы и скомпоновать ее с локальной библиотекой RPC периода выполнения.

Библиотека RPC периода выполнения использует для взаимодействия с транспортным протоколом универсальный интерфейс провайдеров трансnopmaRPC (RPC transport provider interface). Этот интерфейс служит тонкой прослойкой между механизмом RPC и транспортом, которая увязывает операции RPC с функциями, предоставляемыми транспортом. RPC в Windows реализует DLL-модули провайдеров транспорта для именованных каналов, NetBIOS, SPX, TPC/IP и UDP. B Windows Server 2003 провайдер транспорта NetBIOS изъят, но добавлен провайдер для HTTR Аналогичным образом RPC поддерживает работу с различными средствами сетевой защиты.

ПРИМЕЧАНИЕ B Windows 2000 можно написать новые DLL-модули провайдеров для поддержки дополнительных транспортов, но, начиная с Windows XP, встраивание дополнительных DLL провайдеров не поддерживается.

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

Сервер может регистрировать свое имя по адресу, который будет доступен клиенту при поиске. Эта возможность, называемая публикацией имени сервера, реализована в RPC и интегрирована с Active Directory. Если Active Directory не установлена, служба локатора имен возвращается к широковещательной рассылке с использованием NetBIOS. Это позволяет взаимодействовать с системами под управлением Windows NT 4 и дает возможность RPC функционировать на автономных серверах и рабочих станциях.

Защита в RPC

RPC интегрирован с компонентами поддержки защиты (security support providers, SSP), что позволяет клиентам и серверам RPC использовать аутентификацию и шифрование при коммуникационной связи. Когда серверу RPC требуется защищенное соединение, он сообщает библиотеке RPC периода выполнения, какую службу аутентификации следует добавить в список доступных служб аутентификации. A когда клиенту нужно использовать защищенное соединение, он выполняет привязку к серверу. Bo время привязки к серверу клиент должен указать библиотеке RPC службу аутентификации и нужный уровень аутентификации. Различные уровни аутентификации обеспечивают подключение к серверу только авторизованных клиентов, проверку каждого сообщения, получаемого сервером (на предмет того, послано ли оно авторизованным клиентом), контроль за целостностью RPC-сообщений и даже шифрование данных RPC-сообщений. Чем выше уровень аутентификации, тем больше требуется обработки. Клиент также может указывать имя участника безопасности (principal name) для сервера. Участник безопасности (principal) — это сущность, распознаваемая системой защиты RPC Сервер должен зарегистрироваться в SSP под именем участника безопасности, специфичным для SSP.

SSP берет на себя все, что связано с аутентификацией и шифрованием при коммуникационной связи, не только для RPC, но и для Winsock. B Windows несколько встроенных SSP, в том числе Kerberos SSP, реализующий аутентификацию Kerberos v5, SChannel (Secure Channel), реализующий Secure Sockets Layer (SSL), и протоколы TLS (Transport Layer Security). Если SSP не указан, программное обеспечение RPC использует встроенные средства защиты нижележащего транспорта. Одни транспорты, в частности именованные каналы и локальный RPC, имеют такие средства защиты, а другие, например TCP, — нет. B последнем случае RPC при отсутствии указанного SSP выдает небезопасные вызовы.

Еще одна функция защиты RPC позволяет серверу подменять клиент через функцию RpcImpersonateClient. Когда сервер заканчивает выполнение операций, потребовавших подмены клиента собой, он возвращается к использованию своих идентификационных данных защиты вызовом функции RpcRevertToSelf или RpcRevertToSelJEx (подробнее о подмене, или олицетворении, см. главу 8).

Реализация RPC

Реализация RPC изображена на рис. 13-8, где показано, что приложение на основе RPC связано с библиотекой RPC периода выполнения (\Windows\Sys-tem32\Rpcrt4.dll). Последняя предоставляет для интерфейсных RPC-функций приложений функции маршалинга, а также функции для приема и передачи упакованных данных. Библиотека RPC периода выполнения включает процедуры поддержки RPC-взаимодействия через сеть и разновидность RPC под названием локальный RPC. Локальный RPC позволяет двум процессам взаимодействовать в одной системе, при этом библиотека RPC в качестве сетевого API использует LPC в режиме ядра (об LPC см. главу 3). Когда RPC-взаимодей-ствие осуществляется между удаленными системами, библиотека RPC использует API-функции Winsock, именованного канала или Message Queuing.

ПРИМЕЧАНИЕ Message Queuing в Windows Server 2003 не поддерживается в качестве транспорта.

Подсистема RPC (RPCvSS) (\Windows\System32\Rpcss.dll) реализована в виде Windows-сервиса. RPCSS сама является приложением RPC, которое взаимодействует со своими экземплярами на других системах для поиска имен, регистрации и динамического подключения конечной точки (dynamic end-point mapping). (Для упрощения на рис. 13-8 не показана связь RPCSS с библиотекой RPC периода выполнения.)

API-интерфейсы доступа к Web

Чтобы упростить разработку Интернет-приложений, в Windows предусмотрены клиентские и серверные API-интерфейсы доступа к Интернету. C помощью этих API приложения могут предоставлять и использовать сервисы Gopher, FTP и HTTP, не зная внутреннего устройства соответствующих протоколов. Клиентские API включают Windows Internet, также называемый WinInet (позволяет приложениям взаимодействовать с протоколами Gopher, FTP и HTTP), и WinHTTP (дает возможность приложениям взаимодействовать с протоколом HTTP). B определенных ситуациях WinHTTP удобнее WinInet. HTTP — это серверный API, введенный в Windows Server 2003 для поддержки разработки серверных Web-приложений.

WinInet

WinInet поддерживает протоколы Gopher, FTP и HTTP версий 1.0 и 1.1. Этот API делится на наборы под-API, специфичные для каждого протокола. Используя API-функции FTP вроде InternetConnect для подключения к FTP-cep-веру, FtpFindFirstFile и FtpFindNextFile для перечисления содержимого FTP-каталога, а также FtpGetFile и FtpPutFile для приема и передачи файлов, разработчик приложения может не задумываться о деталях, связанных с установлением соединения и форматированием TCP/IP-сообщений для протокола FTP API-функции Gopher и HTTP обеспечивают аналогичный уровень абстракции. WinInet применяется базовыми компонентами Windows, например Windows Explorer и Internet Explorer.

WinHTTP

Текущая версия WinHTTP API — 5.1; она доступна в Windows 2000 с Service Pack 3, в Windows XP и Windows Server 2003- Этот API обеспечивает абстракцию протокола HTTP 1.1 для клиентских HTTP-приложений по аналогии с HTTP API в WinInet. Однако, если WinInet HTTP API предназначен для интерактивных клиентских приложений, то WinHTTP API — для серверных приложений, взаимодействующих с HTTP-серверами. Серверные приложения часто реализуются как Windows-службы без UI, поэтому им не нужны диалоговые окна, которые позволяют выводить API-функции WinInet. Кроме того, WinHTTP API лучше масштабируется и предоставляет средства защиты вроде подмены потоков, недоступные в WinInet API.

HTTP

C помощью HTTP API, реализованного в Windows Server 2003, серверные приложения могут регистрироваться на прием HTTP-запросов с определенных URL, принимать такие запросы и передавать HTTP-ответы. HTTP API включает поддержку SSL (Secure Sockets Layer), чтобы приложения могли обмениваться данными по защищенным HTTP-соединениям. Этот API поддерживает кэширование на серверной стороне, модели синхронного и асинхронного ввода-вывода, а также адресацию по IPv4 и IPv6. HTTP API используется IIS версии 6 (поставляется с Windows Server 2003).

HTTP API, к которому приложения обращаются через библиотеку Httpapi.dll, опирается на драйвер Http.sys режима ядра. Http.sys запускается по требованию при первом вызове HttpInitialize любым приложением. Функция HttpCreateHttpHandle позволяет создавать закрытую очередь запросов, а функция HttpAddUrl — указывать URL-адреса, по которым приложение собирается принимать запросы для обработки. Используя очереди запросов и их зарегистрированные URL, Http.sys дает возможность обслуживать НТТР-запросы на одном порту, например 80, более чем одному приложению.

HttpReceiveHttpRequest принимает входящие запросы, направленные по зарегистрированным URL, zHttpSendHttpRespome передает HTTP-ответы. Обе функции работают в асинхронном режиме, так что приложение может определять, закончена ли какая-то операция, используя GetOverlappedResult или порты завершения ввода-вывода.

Приложения могут использовать Http.sys для кэширования данных в неподкачиваемой физической памяти, вызывая HttpAddToFragmentCache и сопоставляя имя фрагмента с кэшируемыми данными. Ддя выделения неспроецированных страниц физической памяти Http.sys запускает функцию MmAllocate-PagesForMdl, принадлежащую диспетчеру памяти. Когда Http.sys требуется сопоставление виртуального адреса с физической памятью, описываемой элементом кэша (например, если Http.sys копирует данные в кэш или передает их из кэша), он вызывает MmMapLockedPagesSpecifyCache, а по окончании операций — MmUnmapLockedPages. Http.sys хранит кэшируемые данные до тех пор, пока приложение не объявит их недействительными или пока не истечет срок их актуальности, заданный приложением. Http.sys также усекает кэшируемые данные при пробуждении рабочего потока из-за перехода в свободное состояние события, уведомляющего о малом объеме памяти (информацию об этом событии см. в главе 7). Если при вызове HttpSendHttpResponse приложение указывает одно или несколько имен фрагментов, Http.sys передает указатель на данные, кэшируемые в физической памяти, драйверу TCP/ IP и тем самым исключает лишнюю операцию копирования.

Именованные каналы и почтовые ящики

Именованные каналы и почтовые ящики — это API, изначально разработанные Microsoft для OS/2 LAN Manager и впоследствии перенесенные в Windows NT Именованные каналы обеспечивают надежную двустороннюю связь, тогда как почтовые ящики — ненадежную одностороннюю передачу данных. Преимущество почтовых ящиков — в поддержке широковещательной передачи. Оба API используют систему защиты Windows, что позволяет серверам контролировать, какие клиенты могут подключаться к ним.

Серверы назначают именованным каналами и их клиентам имена в соответствии с универсальными правилами именования (Universal Naming Convention, UNC), которые обеспечивают независимый от протоколов способ идентификации ресурсов в Windows-сетях. O реализации UNC-имен мы расскажем позже.

Функционирование именованных каналов

Коммуникационная связь по именованному каналу включает сервер именованного канала и клиент именованного канала. Сервером именованного канала является приложение, создающее именованный канал, к которому подключаются клиенты. Формат имени канала выглядит так: \\Cepвep\Pipe\ ИмяКанала. Элемент Сервер указывает компьютер, на котором работает сервер именованного канала. Элемент Pipe должен быть строкой «Pipe», а Имя-Канала — уникальное имя, назначенное именованному каналу. Уникальная часть имени канала может включать подкаталоги. Пример такого UNC-име-ни канала — \\MyComputer\Pipe\MyServerApp\ConnectionPipe.

Для создания именованного канала сервер использует Windows-функцию CreateNamedPipe. Одним из входных параметров этой функции является указатель на имя канала в форме \\.\Рiре\ИмяКанала, где «\\.\» — псевдоним локального компьютера, определенный в Windows. Функция также принимает необязательный дескриптор защиты, запрещающий несанкционированный доступ к именованному каналу, флаг, указывающий, должен ли канал быть двусторонним или односторонним, параметр, определяющий максимальное число одновременных соединений по данному каналу, и флаг режима работы канала (побайтовой передачи или передачи сообщений).

Большинство сетевых API-функций работают только в режиме побайтовой передачи. Это означает, что переданное сообщение может быть принято адресатом в виде нескольких фрагментов, из которых воссоздается полное сообщение. Именованные каналы, работающие в режиме передачи сообщений, упрощают реализацию приемника, поскольку в этом случае число передач и приемов одинаково, а приемник, разом получая целое сообщение, не должен заботиться об отслеживании фрагментов сообщений.

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

Для подключения к серверу клиенты именованного канала используют Windows-функцию CreateFile или CallNamedPipe, указывая при вызове имя созданного сервером канала. Если сервер вызывает функцию ConnectNamedPipe, профиль защиты клиента и запрошенные им права доступа к каналу (для чтения или записи) сравниваются с дескриптором защиты канала (подробнее об алгоритмах проверки прав доступа см. главу 8). Если клиенту разрешен доступ к именованному каналу, он получает описатель, представляющий клиентскую сторону именованного канала, и функция ConnectNamedPipe, вызванная сервером, завершается.

После того как соединение по именованному каналу установлено, клиент и сервер могут использовать его для чтения и записи данных через Windows-функции ReadFile и WriteFile. Именованные каналы поддерживают как синхронную, так и асинхронную передачу сообщений. Взаимодействие клиента и сервера через именованный канал показано на рис. 13-9.

Уникальная особенность API именованного канала заключается в том, что он позволяет серверу олицетворять клиент с помощью функции ImpersonateNamedPipeClient. O том, как используется олицетворение в клиент-серверных приложениях, см. раздел «Олицетворение» главы 8.

Функционирование почтового ящика

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

Как и именованные каналы, почтовые ящики интегрированы с Windows API. Сервер почтового ящика создает почтовый ящик вызовом CreateMailslot. Входным параметром этой функции является имя в форме «\\.\Mailslot\ИмяПочтовогоЯщика». Сервер может создавать почтовые ящики только на той машине, на которой он работает, а назначаемые им имена почтовых ящиков могут включать подкаталоги. CreateMailslot также принимает необязательный дескриптор защиты, контролирующий доступ клиента к почтовому ящику. Описатели, возвращаемые CreateMailslot, являются перекрытыми.

Это означает, что операции с использованием таких описателей (например, рассылка и получение сообщений) выполняются асинхронно.

Поскольку почтовые ящики поддерживают одностороннюю ненадежную передачу, число параметров CreateMailslot меньше, чем у CreateNamedPipe. После создания почтового ящика сервер просто отслеживает поступающие клиентские сообщения, вызывая функцию ReadFile и указывая описатель, представляющий почтовый ящик.

Клиенты почтового ящика используют формат именования, аналогичный применяемому клиентами именованных каналов, за исключением вариаций, необходимых для широковещательной передачи сообщений всем почтовым ящикам с данным именем в домене клиента или в другом указанном домене. Чтобы послать сообщение в определенный экземпляр почтового ящика, клиент вызывает функцию CreateFile, указывая имя, специфичное для компьютера, например «\\Сервер\Mailslot\ИмяПочтовогоЯщика». (Для представления локального компьютера клиент задает «\\.\».) Если клиент хочет получить описатель, представляющий все почтовые ящики с заданным именем в домене, членом которого он является, он указывает имя в формате «\\*\Mailslot\ ИмяПочтовогоЯщика». Для широковещательной передачи во все почтовые ящики с заданным именем в другом домене используется имя в формате «\\ИмяДомена\Mailslot\ИмяПочтовогоЯщика».

Получив описатель, представляющий клиентскую сторону почтового ящика, клиент посылает сообщения через функцию WriteFile. Реализация почтовых ящиков допускает широковещательную передачу сообщений длиной не более 425 байтов. Если длина сообщения превышает 425 байтов, почтовый ящик использует механизм надежной коммуникационной связи, требующий соединения клиента с сервером по типу «один к одному», что исключает возможность широковещательной передачи. Другая (довольно странная) особенность почтовых ящиков — урезание сообщений с исходной длиной в 425 или 426 байтов до 424 байтов. Таким образом, почтовые ящики непригодны для рассылки сообщений, длина которых превышает 424 байта. Ha рис. 13–10 показан пример широковещательной передачи клиентского сообщения на несколько серверов почтовых ящиков в пределах домена.

Реализация именованных каналов и почтовых ящиков

O тесной интеграции функций именованных каналов и почтовых ящиков с Windows свидетельствует тот факт, что все они реализованы в Kernel32.dll. ReadFile и WriteFile, используемые приложениями для обмена сообщениями через именованные каналы и почтовые ящики, являются основными Windows-функциями ввода-вывода. CreateFile, с помощью которой клиент открывает именованный канал или почтовый ящик, также является стандартной Windows-функцией ввода-вывода. Однако имена, указываемые приложениями при использовании именованных каналов и почтовых ящиков, относятся к пространству имен под управлением драйверов файловых систем именованных каналов (\Windows\System32\Drivers\Npfs.sys) и почтовых ящиков (\Windows\System32\Drivers\Msfs.sys), как показано на рис. 13–11. Драйвер файловой системы именованных каналов создает объект «устройство» \Device\NamedPipe и символьную ссылку на этот объект с именем \Global??\Pipe (\??\Pipe в Windows 2000), а драйвер файловой системы почтовых ящиков создает объект «устройство» \Device\Mailslot и символьную ссылку \Global??\Mailslot (\??\Mailslot в Windows 2000), которая указывает на этот объект. (O каталоге \Global?? диспетчера объектов см. главу 3.) Префикс «\\.\» в именах «\\.\Pipe\…» и «\\.\Mailslot\…», передаваемых CreateFile, транслируется в «\Global??\», чтобы эти имена разрешались через символьную ссылку на объект «устройство». Специальные функции CreateNamedPipe и CreateMailslot используют соответствующие функции ядра NtCreateNamed-PipeFile и NtCreateMailslotFile.

Позже мы обсудим, как драйвер файловой системы участвует в поиске удаленной системы по имени, которое задает удаленный именованный канал или почтовый ящик. Однако, когда именованный канал либо почтовый ящик создается сервером или открывается клиентом, в конечном счете вызывается соответствующий драйвер файловой системы (FSD) на той машине, где находится именованный канал или почтовый ящик. Именованные каналы и почтовые ящики реализованы в виде FSD режима ядра по нескольким причинам. Основной из них является интеграция с пространством имен диспетчера объектов, что позволяет использовать объекты «файл» для представления открытых именованных каналов и почтовых ящиков. Подобная интеграция дает следующие преимущества.

• Используя функции защиты режима ядра, FSD реализуют для именованных каналов и почтовых ящиков стандартную защиту Windows.

• Поскольку FSD интегрированы с пространством имен диспетчера объектов, приложения могут открывать именованный канал или почтовый ящик вызовом функции CreateFile.

• Приложения могут взаимодействовать с именованными каналами и почтовыми ящиками через Windows-функции вроде ReadFile и WriteFile.

• FSD полагаются на диспетчер объектов в поддержке счетчиков описателей и ссылок для объектов «файл», представляющих именованные каналы и почтовые ящики.

• FSD могут реализовать собственные пространства имен каналов и почтовых ящиков, допускающие указание подкаталогов.

Так как взаимодействие через сеть при разрешении имен именованных каналов и почтовых ящиков осуществляется через редиректор, FSD при этом неявно используют протокол CIFS (Common Internet File System). Поскольку CIFS способен работать с TCP/IP, TCP/IP с IPv6 и IPX, именованные каналы и почтовые ящики доступны приложениям, выполняемым в системах, где установлен хотя бы один общий такой протокол. (Сведения о CIFS см. в главе 12.)

ЭКСПЕРИМЕНТ: просмотр пространства имен именованных каналов и наблюдение за активностью таких каналов

Открыть корневой каталог FSD именованных каналов и перечислить его содержимое с помощью Windows API нельзя — для этого нужно воспользоваться сервисами встроенного API. Утилита PipeList (www.sys- internals.com) перечисляет именованные каналы, определенные на компьютере, число созданных экземпляров канала с данным именем и максимальное число каналов, заданное сервером при вызове Create-NamedPipe. Вот пример вывода PipeList.

Из этого листинга ясно, что некоторые системные компоненты используют именованные каналы как механизм связи. Например, канал InitShutdown создан Winlogon для приема удаленных команд на завершение работы, а канал SecLogon — сервисом SecLogon для выполнения операций входа в интересах утилиты Runas. Определить, каким из процессов открыт каждый из этих каналов, можно с помощью утилиты Process Explorer (wwsysinternals.com). Заметьте, что значение Max Instances, равное -1, означает, что на число экземпляров канала с данным именем не накладывается никаких ограничений.

Драйвер фильтра файловой системы Filemon (wwwsysinternals.com) способен подключаться к драйверу файловой системы Npfs.sys или Msfs.sys, что позволяет ему наблюдать за активностью всех именованных каналов или почтовых ящиков в системе. Для подключения Filemon к соответствующему драйверу выберите из меню Drives команду Named Pipes или Mail Slots. Ha иллюстрации ниже показано окно Filemon, в котором сообщается об активности именованных каналов, вызываемой двойным щелчком значка My Network Places (Мое сетевое окружение) на рабочем столе. Заметьте, что сообщения передаются через именованные каналы LSASS и службы рабочей станции.

NetBIOS

До начала 90-х годов NetBIOS (Network Basic Input/Output System) API был самым популярным интерфейсом программирования для персональных компьютеров. NetBIOS поддерживал связь как надежную, ориентированную на логические соединения, так и ненадежную, не требующую логических соединений. Windows поддерживает NetBIOS для совместимости с унаследованными приложениями. Microsoft не рекомендует разработчикам приложений использовать NetBIOS, поскольку существуют куда более гибкие и переносимые API, например именованные каналы и Winsock. NetBIOS в Windows поддерживается протоколами TCP/IP и IPX/SPX.

NetBIOS-имена

NetBIOS использует правила именования, согласно которым компьютерам и сетевым службам назначаются 16-байтовые имена, называемые NetBIOS-именами; 16-й байт в NetBIOS-имени интерпретируется как модификатор, который указывает, является ли имя уникальным или групповым.* Уникальное NetBIOS-имя может быть назначено только одному компьютеру или службе в сети, а групповое имя — нескольким компьютерам или службам. Адресуя сообщение на групповое имя, клиент может вести широковещательную рассылку.

Windows — для поддержки взаимодействия с системами под управлением Windows NT 4 и потребительских версий Windows — автоматически определяет NetBIOS-имя для домена как первые 15 байтов DNS-имени (Domain Name System), назначенного домену администратором. Например, домен mspress.microsoft.com получает NetBIOS-имя mspress. Аналогичным образом Windows требует, чтобы во время установки администратор назначил каждому компьютеру NetBIOS-имя.

Еще одна концепция, используемая в NetBIOS, — номера адаптеров LAN (LANA). Номер LANA присваивается каждому NetBIOS-совместимому протоколу, расположенному на более высоком уровне, чем сетевой адаптер. Так, если в компьютере установлено два сетевых адаптера, доступных для TCP/ IP и NWLink, то в результате будет назначено четыре номера LANA. Номера LANA важны, поскольку приложения NetBIOS должны явно закреплять имена своих сервисов за каждым LANA, через который они готовы принимать клиентские соединения. Если приложение ждет соединений с клиентами по определенному имени, клиенты получат доступ к приложению только через протоколы, для которых зарегистрировано это имя.

Разрешение NetBIOS-имен в IP-адреса описывается в разделе «Windows Internet Name Service» далее в этой главе.

* Здесь авторы допускают неточность. 16-й байт NetBIOS-имени прежде всего является идентификатором типа ресурса. Он указывает сетевой компонент или службу, которая назначила это NetBIOS-имя компьютеру, пользователю или домену. Hy и, кроме того, NetBIOS-имя может быть зарегистрировано как уникальное (принадлежащее одному владельцу) или как групповое (принадлежащее нескольким владельцам). — Прим. перев.

Функционирование NetBIOS

Серверное приложение NetBIOS использует NetBIOS API для перечисления LANA, имеющихся в системе, и назначения каждому из них NetBIOS-имени, представляющего сервис приложения. Если сервер требует логических соединений, он выполняет NetBIOS-команду listen для ожидания попыток подключения клиентов. После того как соединение с клиентом установлено, сервер выполняет функции NetBIOS для передачи и приема данных. Аналогичным образом осуществляется и связь, не требующая логических соединений, но сервер просто принимает сообщения, не устанавливая соединение.

Клиент, ориентированный на логические соединения, устанавливает соединение с сервером NetBIOS, а затем с помощью функций NetBIOS передает и принимает данные. Установленное NetBIOS-соединение также называется сеансом (session). Если клиент хочет посылать сообщения без установления логического соединения, он просто указывает NetBIOS-имя сервера при вызове функции передачи данных.

NetBIOS состоит из набора функций, но все они действуют через один и тот же интерфейс — Netbios. Это наследие тех времен, когда NetBIOS реализовали в виде прерывания MS-DOS. Приложение NetBIOS выполняло прерывание MS-DOS и передавало NetBIOS структуру данных, где задавались все параметры нужной команды. B итоге функция Netbios в Windows принимает единственный параметр, который представляет собой структуру данных с параметрами, специфичными для запрошенного приложением сервиса.

ЭКСПЕРИМЕНТ: просмотр NetBIOS-имен через Nbtstat

Для вывода списка активных сеансов в системе, кэшируемых сопоставлений NetBIOS-имен и IP-адресов, а также NetBIOS-имен, определенных на компьютере, можно использовать встроенную в Windows команду Nbtstat… Ниже приведен пример вывода этой команды с параметром — n, при указании которого выводится список NetBIOS-имен, определенных на компьютере.

Реализация NetBIOS API

Компоненты, реализующие NetBIOS API, показаны на рис. 13–12. Функция Netbios экспортируется приложениям из \Windows\System32\Netapi32.dll. Netapi32.dll открывает описатель драйвера режима ядра под названием эму-nnmopNetBlOS (\Windows\System32\Drivers\Netbios.sys) и выдает Windows-команды DeviceIoControlFile от имени приложения. Эмулятор NetBIOS транслирует команды NetBIOS в команды TDI, посылаемые драйверам протоколов.

Если приложение требует использовать NetBIOS поверх TCP/IP, то эмулятору NetBIOS нужен драйвер NetBT (\Windows\System32\Drivers\Netbt.sys). NetBT отвечает за поддержку семантики NetBIOS, присущей не TCP/IP, а NetBEUI (NetBIOS Extended User Interface), который включался в предыдущие версии Windows. Например, NetBIOS полагается на NetBEUI-поддержку режима передачи данных в виде сообщений и на средства разрешения имен, поэтому драйвер NetBT реализует их поверх протокола TCP/IP. Аналогичным образом драйвер NwLinkNB реализует семантику NetBIOS поверх IPX/SPX.

Другие сетевые API

B Windows входит еще несколько сетевых API, которые используются реже и расположены на более высоком уровне, чем уже рассмотренные API. Изучение всех этих API выходит за рамки книги, но четыре из них — RTC (Real-Time Communications), DCOM (Distributed Component Object Model), Message Queuing и UPnP (Universal Plug and Play) — достаточно важны для функционирования Windows и многих приложений и поэтому заслуживают краткого описания.

RTC

RTC Client API, доступный в Windows XP и Windows Server 2003, позволяет разработчикам создавать приложения, способные устанавливать многорежимные коммуникационные соединения и превращать персональный компьютер (ПК) в центр домашних или деловых коммуникаций. Голосовая и видеосвязь, мгновенный обмен сообщениями (Instant Messaging, IM), поддержка совместной работы — все это становится доступным в одном сеансе коммуникационной связи. Помимо сеансов связи между ПК, этот API позволяет устанавливать сеансы связи «ПК-телефон», «телефон-телефон» или только текстового IM. B сеансах связи между ПК также доступны совместное использование приложений (application sharing) и общая электронная доска (whiteboard).

RTC поддерживает информацию о присутствии (presence information), на основе которой клиенты могут связываться с контактами через сервер-регистратор (registrar server), хранящий информацию о текущих адресах контактов. Адресом контакта может быть ПК или телефон, а в будущем и мобильный телефон, пейджер или другое карманное устройство. Например, если приложение пытается связаться с контактом по его рабочему адресу и информация о присутствии указывает на то, что данный контакт доступен через домашний ПК, RTC автоматически перенаправит соединение на этот адрес. RTC API также обеспечивает невмешательство в частную жизнь, позволяя блокировать определенные вызовы.

DCOM

Microsoft COM API позволяет составлять приложения из компонентов, и каждый компонент представляет собой заменяемый самодостаточный модуль. Любой СОМ-объект экспортирует объектно-ориентированный интерфейс для манипулирования своими данными. Поскольку СОМ-объекты предоставляют четко определенные интерфейсы, разработчики могут реализовать новые объекты для расширения существующих интерфейсов и динамического добавления новой функциональности в приложения.

DCOM — это расширение СОМ, которое дает возможность размещать компоненты приложения на разных компьютерах, при этом приложению безразлично, что один СОМ-объект находится на локальном компьютере, а другой — на каком-то компьютере в локальной сети. Таким образом, DCOM упрощает разработку распределенных приложений. DCOM не является автономным API — в своей работе он опирается на RPC.

Message Queuing

Message Queuing представляет собой универсальную платформу для разработки распределенных приложений, использующих преимущества свободно связанного обмена сообщениями (loosely coupled messaging). Поэтому Message Queuing является также API-интерфейсом и инфраструктурой передачи сообщений. Гибкость Message Queuing определяется тем, что его очереди служат репозитариями сообщений, в которые отправители помещают сообщения для посылки получателям и из которых получатели извлекают адресованные им сообщения. Отправителям и получателям не требуется ни устанавливать соединения, ни работать в одно и то же время. A это позволяет асинхронно обмениваться сообщениями, не устанавливая прямых соединений.

Примечательная особенность Message Queuing — его интеграция с MTS (Microsoft Transaction Server) и SQL Server, что дает возможность Message Queuing участвовать в транзакциях, координируемых MS DTC (Microsoft Distributed Transaction Coordinator). Используя MS DTC c Message Queuing, можно разрабатывать для трехуровневых приложений надежные компоненты, отвечающие за обработку транзакций.

UPnP

Universal Plug and Play (UPnP) — это распределенная, открытая сетевая архитектура для поддержки соединений с интеллектуальными устройствами и точками управления (control points), подключенными к домашним сетям, интрасетям или напрямую к Интернету. Она построена на принятых стандартах и опирается на существующие технологии TCP/IP и Web. UPnP не требует конфигурирования и поддерживает автоматическое распознавание широкого спектра устройств. Она позволяет устройству динамически подключаться к сети, получать IP-адрес и сообщать о своих возможностях, когда поступает соответствующий запрос. Точки управления могут применять Control Point API в сочетании с технологией UPnP для определения присутствия и возможностей остальных устройств в сети. Устройство может автоматически выходить из сети, если оно больше не используется.

Поддержка нескольких редиректоров

У приложений есть два способа просмотра удаленных ресурсов или доступа к ним. Один из них заключается в использовании стандарта UNC и прямой адресации к удаленным ресурсам через Windows-функции, а второй — в применении Windows Networking (WNet) API для перечисления компьютеров и экспортируемых ими ресурсов. Оба подхода опираются на возможности редиректора. Для доступа клиентов к CIFS-серверам Microsoft поставляет редиректор CIFS, у которого есть компонент режима ядра (FSD редиректора) и компонент пользовательского режима (служба рабочей станции). Microsoft также предоставляет редиректор, способный обращаться к ресурсам серверов Novell NetWare, а сторонние разработчики могут добавлять в

Windows собственные редиректоры. B этом разделе мы расскажем о программном обеспечении, которое решает, какой редиректор следует вызвать для обработки запроса на удаленный ввод-вывод. За это отвечают следующие компоненты.

• Маршрутизатор многосетевого доступа (multiple provider router, MPR) Это DLL (\Windows\System32\Mpr.dll), определяющая, к какой сети следует обратиться, когда приложение использует Windows WNet API для просмотра удаленной файловой системы.

• Многосетевой UNC-провайдер (multiple UNC provider, MUP) Драйвер (\Windows\System32\Drivers\Mup.sys), определяющий, к какой сети следует обратиться, когда приложение использует Windows API ввода-вывода для открытия удаленных файлов.

Маршрутизатор многосетевого доступа

Windows-функции WNet позволяют приложениям (включая Windows Explorer и My Network Places) подключаться к сетевым ресурсам (файлам и принтерам), а также просматривать содержимое удаленных файловых систем любого типа. Так как этот API предназначен для работы с различными сетями и по разным протоколам, необходимо специальное программное обеспечение, способное посылать запросы по сети и правильно интерпретировать результаты, получаемые от удаленных серверов. Это программное обеспечение показано на рис. 13–13.

Провайдер (provider) — это программный компонент, позволяющий Windows выступать в качестве клиента какого-либо удаленного сервера. B число операций, выполняемых провайдером WNet, входят установление и разрыв сетевых соединений, удаленная печать и передача данных. Встроенный провайдер WNet включает DLL, службу рабочей станции и редиректор. Поставщики других сетей должны предоставлять только DLL и редиректор.

Когда приложение вызывает некую функцию WNet, этот вызов передается непосредственно MPR DLL. MPR принимает вызов и определяет, какой из провайдеров WNet распознает запрошенный ресурс. Все DLL провайдеров, расположенные ниже MPR, предоставляют набор стандартных функций, в совокупности называемых интерфейсом сетевого доступа (network provider interface). Этот интерфейс позволяет MPR определить, к какой сети пытается обратиться приложение, и направить вызов соответствующему провайдеру WNet. Провайдером службы рабочей станции является \Windows\System32\Ntlanman.dll, что указывается в параметре ProviderPath в разделе реестра HKLM\SYSTEM\CurrentControlSet\Services\LanManWorkstation\NetworkProvider.

Рис. 13–14. Редактор порядка провайдеров (служб доступа к сети)

Когда MPR вызывается для подключения к удаленному сетевому ресурсу API-функцией WNetAddConnection, он просматривает в реестре параметр HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\HwOrder\ProviderOrder, чтобы определить, какие провайдеры сетей загружены. Далее он опрашивает их в том порядке, в каком они перечислены в реестре, и делает это до тех пор, пока один из них не распознает сетевой ресурс или пока все они не будут опрошены. Параметр ProviderOrder можно изменить через диалоговое окно Advanced Settings (Дополнительные параметры), показанное на рис. 13–14. (B системе, в которой был сделан этот экранный снимок, установлен только один провайдер.) B Windows 2000 или при настройке меню Start (Пуск) в традиционном стиле это диалоговое окно вызывается из меню Advanced (Дополнительно) апплета Network Connections (Сетевые подключения), который запускается несколькими способами. Вы можете, например, щелкнуть правой кнопкой мыши значок My Network Places (Мое сетевое окружение) на рабочем столе и выбрать из контекстного меню команду Properties (Свойства), либо открыть меню Start (Пуск), затем подменю Settings (Настройка) и выбрать команду Network Connections (Сетевые подключения).

Функция WNetAddConnection может также назначить удаленному ресурсу букву диска или имя устройства. B этом случае она направляет вызов соответствующему компоненту сетевого доступа. Тот в свою очередь создает объект «символьная ссылка» в пространстве имен диспетчера объектов, и этот объект увязывает данную букву диска с редиректором нужной сети (т. е. с удаленным FSD).

Ha рис. 13–15 показан каталог \?? в системе Windows 2000, в котором вы заметите несколько букв диска, представляющих соединения с удаленными файловыми ресурсами. Как видите, редиректор создает объект «устройство» с именем \Device\LanmanRedirector, а дополнительный текст, который входит в значение символьной ссылки, сообщает редиректору, какому удаленному ресурсу соответствует буква диска. Когда пользователь открывает X: \ Book\Chapl3.doc, редиректору передается неразобранная часть пути, которая разрешается через символьную ссылку как «;X:0\dual\e\Book\ Chapl3doc». Редиректор отмечает, что данный ресурс расположен на общем диске E сервера dual

Как и встроенный редиректор, другие редиректоры тоже создают объект «устройство» в пространстве имен диспетчера объектов в процессе своей загрузки и инициализации. После этого, когда WNet или другой API обращается к диспетчеру объектов для открытия ресурса, расположенного в другой сети, диспетчер использует данный объект «устройство» как точку входа в удаленную файловую систему Он вызывает метод разбора, принадлежащий диспетчеру ввода-вывода и сопоставленный с объектом, для поиска FSD редиректора, способного обработать данный запрос (о драйверах файловых систем см. главу 12).

Многосетевой UNC-провайдер

Многосетевой UNC-провайдер (Multiple UNC Provider, MUP) — сетевой компонент, сходный с MPR. Он обрабатывает запросы ввода-вывода (адресованные к файлам или устройствам) с UNC-именами (именами, которые начинаются с символов \\, указывающих, что данный ресурс находится в сети). MUP, как и MPR, определяет; какой локальный редиректор распознает удаленный ресурс. Ho MUP в отличие от MPR является драйвером устройства (загружаемым при загрузке системы), который выдает запросы на ввод-вывод драйверам более низкого уровня, в данном случае — редиректорам, как показано на рис. 13–16. Mup.sys также содержит клиентскую реализацию Distributed File System (DFS). Клиент DFS включен по умолчанию, и его можно отключить, присвоив DWORD-параметру реестра HKLM\System\CurrentCont-rolSet\Services\Mup\DisableDfs значение 1.

При загрузке MUP создает объект «устройство» с именем \Device\Mup. Когда сетевой редиректор вроде CIFS загружает редиректор, тот создает именованный объект «устройство» (скажем, \Device\LanmanRedirector) и регистрируется в MUP как UNC-провайдер вызовом функции FsRtlRegister.

UncProvider. Если этот редиректор — первый из зарегистрированных и если поддержка DFS-клиента в MUP отключена, то FsRtlRegisterUncProvider создает символьную ссылку \??\UNC, которая указывает на объект «устройство» редиректора; в ином случае MUP настраивает символьную ссылку \Global??\ UNC (\??\UNC в Windows 2000) так, чтобы она указывала на его объект «устройство», \Device\MUP

Драйвер MUP активизируется, когда приложение впервые пытается открыть удаленный файл или устройство по UNC-имени (а не по букве сетевого диска). Получив запрос на ввод-вывод с UNC-путем, Kernel32.dll (экспортирующая API-функции файлового ввода-вывода) на клиентской стороне добавляет переданный в запросе UNC-путь к строке \Global??\UNC после чего вызывает системный сервис NtCreateFile для открытия файла.

Если зарегистрирован только один провайдер сети, то \Global??\UNC разрешается в объект «устройство», представляющий драйвер, и запрос обрабатывается этим драйвером. При наличии нескольких зарегистрированных провайдеров \Global??\UNC разрешается в \Device\MUP, и MUP должен определить, какой провайдер будет обрабатывать данный запрос.

Когда драйвер MUP принимает запрос ввода-вывода и клиент DFS включен, MUP сначала определяет, соответствует ли указанный путь DFS-пути (DFS-пути тоже форматируются по стандарту UNC), и, если да, сам обрабатывает запрос. Если клиент DFS отключен или путь не соответствует DFS-пути, MUP считывает параметр реестра HKLM\SYSTEM\CurrentControlSet\ Control\NetworkProvider\Order\ProviderOrder, чтобы определить приоритет провайдеров сетей, зарегистрированных через FsRtlRegisterUncProvider. Затем MUP поочередно опрашивает провайдеры в том порядке, в каком они перечислены в данном параметре реестра, до тех пор, пока один из них не сообщит, что он распознал данный путь, или пока не будут опрошены все имеющиеся провайдеры. MUP игнорирует те редиректоры, которые указаны в параметре ProviderOrder, но не зарегистрированы. Когда один из редиректоров распознает путь, он сообщает, какая часть пути уникальна именно для него. Например, если путем является строка «\\WIN2K3SERVER\PUBLIC\Win-dowsinternals\Chapl3.doc», редиректор может распознать и объявить своей подстроку «\\WIN2K3SERVER\PUBLIC». Драйвер MUP кэширует эту информацию и впоследствии пересылает запросы, начинающиеся с данной подстроки, непосредственно этому редиректору, пропуская стадию опроса. Кэш драйвера MUP хранит данные в течение определенного периода, поэтому через некоторое время сопоставление подстроки с данным редиректором становится недействительным.

Разрешение имен

Разрешение имен (name resolution) — это процесс, в ходе которого символьное имя вроде Mycomputer или www.microsoft.com транслируется в числовой адрес типа 192.l68.1.1, распознаваемый стеком протоколов. B этом разделе описываются два TCP/IP-протокола разрешения имен, предоставляемые Windows, — DNS (Domain Name System) и WINS (Windows Internet Name Service).

DNS

DNS (Domain Name System) — стандарт трансляции имен в Интернете (например, www.microsoft.com) в соответствующие IP-адреса. Сетевое приложение, которому требуется разрешить DNS-имя в IP-адрес, использует TCP/IP для передачи серверу запроса на поиск DNS-имени. DNS-серверы реализуют распределенную базу данных сопоставлений имен и IP-адресов, используемых при разрешении. Каждый сервер обслуживает разрешение имен для определенной зоны. Подробное описание DNS не входит в задачи этой книги, но DNS представляет собой основной протокол разрешения имен в Windows.

DNS-сервер реализован в виде Windows-сервиса (\Windows\System32\ Dns.exe), который входит в состав серверных версий Windows. DNS-сервер в стандартной реализации использует в качестве базы данных текстовый файл, но DNS-сервер в Windows может быть сконфигурирован на хранение зонной информации в Active Directory.

WlNS

Сетевая служба WINS (Windows Internet Name Service) хранит и поддерживает сопоставления между NetBIOS-именами и IP-адресами, используемые TCP/IP-приложениями на основе NetBIOS. Если WINS не установлена, NetBIOS разрешает имена, рассылая широковещательные сообщения в локальной подсети. Заметьте, что NetBIOS-имена вторичны по отношению к DNS-именам в случае приложений Windows Sockets: имена компьютеров регистрируются и разрешаются сначала через DNS. Windows возвращается к NetBIOS-именам, только если разрешение имени через DNS заканчивается неудачно.

Драйверы протоколов

Драйверы сетевых API должны принимать запросы, адресованные к API, и транслировать их в низкоуровневые запросы сетевых протоколов для передачи по сети. Драйверы API выполняют реальную трансляцию с помощью драйверов транспортных протоколов в режиме ядра. Отделение API от нижележащих протоколов придает сетевой архитектуре гибкость, позволяющую каждому API использовать множество различных протоколов. B Windows входят следующие драйверы протоколов: TCP/IP, TCP/IP с IPv6, NWLink и Apple-Talk. Ниже дается краткое описание каждого из этих протоколов.

• Взрывное развитие Интернета и популярность TCP/IP обусловили статус этих протоколов как основных в Windows. TCP/IP был разработан DARPA (Defense Advanced Research Projects Agency) в 1969 году как фундамент Интернета, поэтому характеристики TCP/IP (поддержка маршрутизации и хорошая производительность в WAN) благоприятствуют его использованию в глобальных сетях. TCP/IP — основной стек протоколов в Windows. Он устанавливается по умолчанию, и его нельзя удалить.

• 4-байтовые сетевые адреса, используемые протоколом IPv4 в стандартном стеке протоколов TCP/IP, ограничивают число общедоступных IР-адресов примерно до 4 миллиардов. И это становится серьезной проблемой, поскольку в Интернете появляется все больше и больше устройств, таких как сотовые телефоны и КПК. По этой причине начинается внедрение протокола IPv6, в котором каждый адрес имеет 16 байтов. B Windows XP (Service Pack 1 и выше) и Windows Server 2003 включен стек TCP/IP, \Win-dows\System32\Drivers\Tcpip6.sys, реализующий IPv6. Windows-реализация IPv6 совместима с сетями на основе IPv4 за счет туннелирования.

• NWLink состоит из протоколов Novell IPX и SPX. NWLink включен в Windows для взаимодействия с серверами Novell NetWare.

• Протокол AppleTalk используется в сетях Apple Macintosh; его поддержка позволяет Windows взаимодействовать со службами доступа к файлам и принтерам в сетях на основе AppleTalk.

B Windows транспорты TDI в общем случае реализуют все протоколы, сопоставленные с основным стеком протоколов. Например, драйвер TCP/IP IPv4 (\Windows\System32\Drivers\Tcpip.sys) реализует протоколы TCP, UDP, IP, ARP, ICMP и IGMP Для представления конкретных протоколов транспорт TDI обычно создает объекты «устройство», что позволяет клиентам получать объект «файл», представляющий нужный протокол, и выдавать ему запросы на сетевой ввод-вывод с использованием IRP Драйвер TCP/IP создает несколько объектов «устройство» для представления различных протоколов, доступных клиентам TDI: \Device\Tcp, \Device\Udp и \Device\Ip, а также (в Windows XP и Windows Server 2003) \Device\Rawip и \Device\Ipmulticast.

ЭКСПЕРИМЕНТ: просмотр объектов «устройство», принадлежащих TCP/IP

C помощью отладчика ядра можно изучить эти объекты в работающей системе. Команда !drvobj позволяет узнать адрес каждого объекта «устройство» драйвера, a !devobj — просмотреть имя и другие сведения о конкретном объекте.

Microsoft определила стандарт TDI (Transport Driver Interface), чтобы драйверам сетевых API не приходилось использовать отдельные интерфейсы для каждого необходимого им транспортного протокола. Как уже говорилось, интерфейс TDI фактически представляет собой правила форматирования сетевых запросов в IRP, а также выделения сетевых адресов и коммуникационных соединений. Транспортные протоколы, отвечающие стандарту TDI, экспортируют интерфейс TDI своим клиентам, в число которых входят драйверы сетевых API, например AFD и редиректор. Транспортный протокол, реализованный в виде драйвера устройства Windows, называется транспортом TDI. Поскольку транспорты TDI являются драйверами устройств, они преобразуют получаемые от клиентов запросы в формат IRP.

Интерфейс TDI образуют функции поддержки из библиотеки \Windows\ System32\Drivers\Tdi.sys вместе с определениями, включаемыми разработчиками в свои драйверы. Модель программирования TDI очень напоминает таковую в Winsock. Устанавливая соединение с удаленным сервером, клиент TDI выполняет следующие действия.

1. Чтобы выделить адрес, клиент создает и форматирует TDI IRP-пакет address open. Транспорт TDI возвращает объект «файл», который представляет адрес и называется объектом адреса (address object). Эта операция эквивалентна вызову Winsock-функции bind.

2. Далее клиент создает и форматирует TDI IRP-пакет connection open, a транспорт TDI возвращает объект «файл», который представляет соединение и называется объектом соединения (connection object). Эта операция эквивалентна вызову Winsock-функции socket.

3. Клиент сопоставляет объект соединения с объектом адреса с помощью TDI IRP-пакета associate address (для этой операции эквивалентных функций Winsock нет).

4. Клиент TDI, соглашающийся установить удаленное соединение, выдает TDI IRP-пакет listen, указывая для объекта соединения максимальное число подключений. После этого он выдает TDI IRP-пакет accept, обработка которого заканчивается либо установлением соединения с удаленной системой, либо ошибкой. Эти операции эквивалентны вызову Winsock-функций listen и accept.

5. Клиент TDI, которому нужно установить соединение с удаленным сервером, выдает TDI IRP-пакет connect, указывая объект соединения, выполняемый транспортом TDI после установления соединения или появления ошибки. Выдача TDI IRP-пакета connect эквивалентна вызову Winsock-функции connect.

TDI также поддерживает коммуникационную связь, не требующую логических соединений, для протоколов соответствующего типа, например для UDP. Кроме того, TDI предоставляет клиенту TDI средства для регистрации в транспортах TDI своих функций обратного вызова по событиям (event callbacks) (т. е. функций, вызываемых напрямую). Например, при получении данных через сеть транспорт TDI может вызвать зарегистрированную клиентом функцию обратного вызова для приема данных. Поддержка функций обратного вызова на основе событий позволяет транспорту TDI уведомлять своих клиентов о сетевых событиях, а клиенты, использующие такие функции, могут не выделять ресурсы для приема данных из сети, поскольку им доступно содержимое буферов, предоставляемых драйвером протокола TDL.

ЭКСПЕРИМЕНТ: наблюдаем активность, связанную с TDI

Утилита TDImon (wwwsysinternats.com) является разновидностью драйвера фильтра, который подключается к объектам «устройство» \Device\Tcp и \Device\Udp, создаваемым драйвером TCP/IP. После подключения TDImon может наблюдать за каждым IRP, выдаваемым клиентами TDI своим протоколам. TDImon также может отслеживать функ ции обратного вызова по событиям, перехватывая запросы на их регистрацию от клиентов TDI Драйвер TDImon посылает информацию об активности TDI своему графическому пользовательскому интерфейсу, который и отображает эти сведения (время операции, тип активности TDI, локальный и удаленный адреса TCP-соединения или локальный адрес конечной точки UDP, код статуса IRP и др.). Ниже приведен экранный снимок окна TDImon, в котором ведется мониторинг активности TDI при просмотре Web-страницы в Internet Explorer.

Как доказательство «врожденной» асинхронности операций TDI, в колонке Result выводятся сообщения «PENDING». Это говорит о том, что операция инициирована, но обработка IRP, вызвавшего ее выполнение, еще не завершена. Чтобы было видно, в каком порядке одни операции завершаются относительно начала других, факт выдачи каждого IRP или обращения к функции обратного вызова отмечается своим порядковым номером. Если до завершения обработки данного IRP генерируются или завершаются другие IRP, эти факты также отмечаются соответствующими порядковыми номерами, которые показываются в колонке Result. Например, на нашей иллюстрации IRP 1278 завершился после генерации IRP 1279, поэтому в колонке Result для IRP 1278 выводится число 1280.

Расширения TCP/IP

Ряд сетевых сервисов Windows расширяет базовые сетевые возможности драйвера TCP/IP за счет применения драйверов-надстроек, интегрируемых с драйвером TCP/IP через закрытые интерфейсы. K числу таких сервисов относятся трансляция сетевых адресов (NAT), IP-фильтрация, подключение IP-ловушек (IP-hooking) и IP-безопасность (IPSec). Ha рис. 13–17 показано, как эти расширения связаны с драйвером TCP/IP.

Трансляция сетевых адресов

Трансляция сетевых адресов (network address translation, NAT) представляет собой сервис маршрутизации, позволяющий отображать несколько закрытых IP-адресов на один общий IP-адрес, видимый в Интернете. Без NAT для коммуникационной связи с Интернетом каждому компьютеру в локальной сети (LAN) пришлось бы назначать свой IP-адрес, видимый в Интернете. NAT дает возможность назначить такой IP-адрес только одному из компьютеров в локальной сети и подключать остальные компьютеры к Интернету через него. NAT по мере необходимости транслирует LAN-адреса в общий IР-адрес, перенаправляя пакеты из Интернета на соответствующий компьютер в локальной сети.

Компоненты NAT в Windows — драйвер устройства NAT (\Windows\System32\Drivers\Ipnat.sys), взаимодействующий со стеком TCP/IP, а также редакторы, с помощью которых возможна дополнительная обработка пакетов (помимо трансляции адресов и портов). NAT может быть установлен как маршрутизирующий протокол через оснастку Routing And Remote Access (Маршрутизация и удаленный доступ) консоли MMC или настройкой Интернет-соединения на общее использование через апплет Network Connections (Сетевые подключения). (Более широкие возможности в настройке NAT предоставляет оснастка Routing And Remote Access.)

IP-фильтрация

B Windows 2000, Windows XP и Windows Server 2003 есть минимальные базовые средства IP-фильтрации, позволяющие пропускать пакеты только по определенным портам или IP-протоколам. Хотя эти средства в какой-то мере защищают компьютер от несанкционированного доступа из сети, их недостаток в том, что они статичны и не предусматривают возможность автоматического создания новых фильтров для трафика, инициируемого работающими на компьютере приложениями.

B Windows XP введен персональный брандмауэр — Windows Firewall, возможности которого шире, чем у базовых средств фильтрации. Windows Firewall реализует брандмауэр с поддержкой состояний (stateful firewall), который отслеживает и различает трафик, генерируемый TCP/IP, и трафик, поступающий из LAN и Интернета. Когда вы включаете Windows Firewall для какого-либо сетевого интерфейса, весь незатребованный входящий трафик по умолчанию отбрасывается. Приложение или пользователь может определить исключения, чтобы сервисы, работающие на данном компьютере (вроде службы доступа к общим файлам и принтерам), были доступны с других компьютеров.

Сервис Windows Firewall/ICS (Internet Connection Sharing), выполняемый в процессе Svchost, передает правила исключения, определенные через пользовательский интерфейс Windows Firewall, драйверу IPNat. B режиме ядра Windows Firewall реализован в том же драйвере (\Windows\System32\Drivers\Ipnat.Sys), который реализует трансляцию сетевых адресов (NAT). Драйвер NAT регистрируется в драйвере TCP/IP как драйвер ловушки брандмауэра (firewall hook). Драйвер TCP/IP выполняет функции обратного вызова каждой зарегистрированной ловушки брандмауэра в ходе обработки входящих и исходящих IP-пакетов. Функция обратного вызова может выступать в роли NAT, модифицируя адреса источника и получателя в пакете, или в роли брандмауэра, возвращая код состояния, указывающий TCP/IP отбросить пакет.

Хотя Ipnat реализует Windows Firewall с применением интерфейса ловушек брандмауэра TCP/IP, Microsoft рекомендует сторонним разработчикам реализовать поддержку фильтрации пакетов в виде промежуточного драйвера NDIS (о нем мы еще расскажем в этой главе).

IP-фильтр и ловушка фильтра

B Windows XP и Windows Server 2003 включен API фильтрации пакетов пользовательского режима, а также драйвер фильтра IP, \Windows\System32\ Drivers\Ipfltrdrv.sys, которые позволяют приложениям управлять входящими и исходящими пакетами. Кроме того, драйвер фильтра IP дает возможность максимум одному драйверу регистрироваться в качестве драйвера ловушки фильтра (filter hook). TCP/IP — по аналогии с тем, как он взаимодействует с драйверами ловушек брандмауэра, — выполняет функцию, которую указывает драйвер фильтра IP, а это позволяет IP-фильтру отбрасывать или модифицировать пакеты. B свою очередь IP-фильтр обращается к функции обратного вызова, заданной драйвером ловушки фильтра, и тем самым передает изменения или запрос на отклонение пакета драйверу TCP/IP.

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

IP-безопасность

IP-безопасность (Internet Protocol Security, IPSec) интегрирована со стеком TCP/IP и защищает одноадресные (unicast) IP-данные от перехвата и несанкционированной модификации, подмены IP-адресов и атак через посредника (man-in-the-middle attacks). IPSec обеспечивает глубоко эшелонированную оборону от сетевых атак с недоверяемых компьютеров, от атак, которые могут привести к отказу в обслуживании, от повреждения данных, кражи информации и учетных данных пользователей, а также от попыток захватить административный контроль над серверами, другими компьютерами и сетью. Эти цели реализуются за счет сервисов защиты на основе шифрования, протоколов безопасности и динамического управления ключами. При обмене одноадресными IP-пакетами между доверяемыми хостами IPSec поддерживает следующую функциональность:

• аутентификацию источника данных — проверку источника IP-пакета и запрет несанкционированного доступа к данным;

• целостность данных — защиту IP-пакета от модификации в процессе доставки и распознавание любых изменений;

• конфиденциальность — содержимое IP-пакетов зашифровывается перед отправкой, благодаря чему их содержимое может быть расшифровано только указанным адресатом;

• защиту от повторений пакетов (anti-replay, или replay protection) — гарантирует уникальность каждого IP-пакета и невозможность его повторного использования. Злоумышленник, даже если он сумеет перехватить IP-пакеты, не сможет повторно использовать их для установления сеанса связи или неавторизованного доступа к информации. Для защиты от сетевых атак вы можете настроить IPSec на фильтрацию пакетов хостом (host-based packet filtering) и разрешать соединения только с доверяемыми компьютерами. После настройки на фильтрацию пакетов хостом IPSec может разрешать или блокировать определенные виды одноадресного IP-трафика, исходя из адресов источника и получателя, заданных протоколов и портов. Поскольку IPSec интегрирован с IP-уровнем (уровнем 3) стека TCP/IP и действует на все приложения, вам не понадобится настраивать параметры безопасности индивидуально для каждого приложения, работающего с TCP/IP.

B среде Active Directory групповая политика позволяет настраивать домены, сайты и организационные единицы (organizational units, OU), а политики IPSec можно закреплять за нужными объектами групповой политики (Group Policy Objects, GPO). B качестве альтернативы можно конфигурировать и применять локальные политики IPSec. Политики IPSec хранятся в Active Directory, а копия параметров текущей политики поддерживается в кэше в локальном реестре. Локальные политики IPSec хранятся в реестре локальной системы.

Для установления доверяемого соединения IPSec использует взаимную аутентификацию (mutual authentication), при этом поддерживаются следующие методы аутентификации: Kerberos версии 5, сертификат открытого ключа X.509 версии 3 или на основе общего ключа (preshared key).

Windows-реализация IPSec основана на RFC, относящихся к IPSec. Архитектура Windows IPSec включает IPSec Policy Agent (Агент политики IP-безопасности), протокол Internet Key Exchange (IKE) и драйвер IPSec.

• Агент политики IP-безопасности Выполняется как сервис в процессе LSASS (о LSASS см. главу 8). B ММС-оснастке Services (Службы) в списке служб он отображается как IPSEC Services (Службы IPSEC). Агент политики IP-безопасности получает политику IPSec из домена Active Directory или из локального реестра и передает фильтры IP-адресов драйверу IPSec, а параметры аутентификации и безопасности — IKE.

• IKE Ожидает от драйвера IPSec запросы на согласование сопоставлений безопасности (security associations, SA), согласовывает SA, а потом возвращает параметры SA драйверу IPSec. SA — это набор взаимно согласованных параметров политики IPSec и ключей, определяющий службы и механизмы защиты, которые будут использоваться при защищенной коммуникационной связи между двумя равноправными хостами с IPSec. Каждое SA является односторонним, или симплексным, соединением, которое защищает передаваемый по нему трафик. IKE согласует SA основного и быстрого режимов, когда от драйвера IPSec поступает соответствующий запрос. SA основного режима IKE (или ISAKMP) защищает процесс согласования, выполняемый IKE, a SA быстрого режима (или IPSec) — трафик приложений.

• Драйвер IPSec Это драйвер устройства (\Windows\System32\Drivers\ Ipsec.sys), который привязывается к драйверу TCP/IP и который обрабатывает пакеты, передаваемые через драйвер TCP/IP. Драйвер IPSec отслеживает и защищает исходящий одноадресный IP-трафик, а также отслеживает, расшифровывает и проверяет входящие одноадресные 1Р-пакеты. Этот драйвер принимает фильтры от агента политики 1Р-безопасности, а затем пропускает, блокирует или защищает пакеты в соответствии с критериями фильтров. Для защиты трафика драйвер IPSec использует параметры активного SA либо запрашивает создание новых SA. ММС-оснастка IP Security Policy Management (Управление политикой безопасности IP) позволяет создавать политику IPSec и управлять ею. C помощью этой оснастки можно создавать, изменять и сохранять локальные политики IPSec или политики IPSec на основе Active Directory, а также модифицировать политику IPSec на удаленных компьютерах. B Windows XP и Windows Server 2003, после того как защищенное IPSec-соединение установлено, вы можете отслеживать информацию IPSec для локального и удаленных компьютеров через ММС-оснастку IP Security Monitor (Монитор IP-безопасности).

Драйверы NDIS

Когда драйверу протокола требуется получить или отправить сообщение в формате своего протокола, он должен сделать это с помощью сетевого адаптера. Поскольку ожидать от драйверов протоколов понимания нюансов работы каждого сетевого адаптера нереально (на рынке предлагается несколько тысяч моделей сетевых адаптеров с закрытой спецификацией), производители сетевых адаптеров предоставляют драйверы устройств, которые принимают сетевые сообщения и передают их через свои устройства. B 1989 году компании Microsoft и 3Com совместно разработали спецификацию Network Driver Interface Specification (NDIS), которая определяет аппаратно-независимое взаимодействие драйверов протоколов с драйверами сетевых адаптеров. Драйверы сетевых адаптеров, соответствующие NDIS, называются драйверами NDIS или минипорт-драйверами NDIS. C Windows 2000 поставляется NDIS версии 5, а с Windows XP и Windows Server 2003 — версии 5.1.

Библиотека NDIS (\Windows\System32\Drivers\Ndis.sys) реализует пограничный уровень между транспортами TDI (в типичном случае) и драйверами NDIS. Как и Tdi.sys, библиотека NDIS является вспомогательной и используется клиентами драйверов NDIS для форматирования команд, посылаемых этим драйверам. Драйверы NDIS взаимодействуют с библиотекой, чтобы получать запросы и отвечать на них. Взаимосвязи между компонентами, имеющими отношение к NDIS, показаны на рис. 13–18.

Одна из целей Microsoft при разработке сетевой архитектуры состояла в том, чтобы производителям сетевых адаптеров было легче разрабатывать драйверы NDIS и переносить их код между потребительскими версиями Windows и Windows 2000. Таким образом, библиотека NDIS предоставляет драйверам не просто вспомогательные пограничные процедуры NDIS, а целую среду выполнения драйверов NDIS. Последние не являются истинными драйверами Windows, поскольку не могут функционировать без инкапсулирующей их библиотеки NDIS. Этот инкапсулирующий уровень является настолько плотной оболочкой драйверов NDIS, что они не принимают и не обрабатывают IRR Вместо этого драйверы протоколов TDI вызывают функцию NdisAllocatePacket в библиотеке NDIS и передают пакеты минипорту NDIS, вызывая соответствующую NDIS-функцию, например NdisSend. По умолчанию драйверам NDIS также не приходится заботиться о реентерабельности, когда библиотека NDIS вызывает драйвер с новым запросом до того, как он успел обработать предыдущий запрос. Освобождение от поддержки реентерабельности кода означает, что создатели драйверов NDIS могут не думать о сложных проблемах синхронизации, которые еще больше усложняются в многопроцессорных системах.

ПРИМЕЧАНИЕ Библиотека NDIS использует для представления запросов ввода-вывода NDIS-пакеты, а не IRR Транспорты TDI создают NDIS-пакет вызовом NdisAllocatePacket, после чего пакет передается минипорту NDIS вызовом одной из функций библиотеки NDIS (например, NdisSend).

Хотя сериализация обращений к драйверам NDIS, осуществляемая библиотекой NDIS, упрощает разработку, она может помешать масштабированию многопроцессорных систем. Некоторые операции стандартных драйверов NDIS 4 (версия библиотеки NDIS 4 из Windows NT 4) плохо масштабируются в многопроцессорных системах. B NDIS 5 разработчики получили возможность отказаться от такой сериализации. Драйвер NDIS 5 может сообщить библиотеке NDIS, что сериализация ему не нужна, и тогда библиотека NDIS переправляет драйверу запросы по мере получения соответствующих IRP B этом случае ответственность за управление параллельными запросами ложится на драйвер NDIS, но отказ от сериализации окупается повышением производительности в многопроцессорных системах.

NDIS 5 также обеспечивает следующие преимущества.

• Драйверы NDIS могут сообщать, активна ли несущая сетевая среда, что позволяет Windows выводить на панель задач значок, показывающий, подключен ли компьютер к сети. Эта функция также позволяет протоколам и другим приложениям быть в курсе этого состояния и соответствующим образом реагировать. Например, транспорт TCP/IP будет использовать эту информацию, чтобы определять, когда нужно заново оценивать информацию об адресах, получаемую им от DHCP

• Аппаратное ускорение TCP/IP-операций (TCP/IP task offload) позволяет минипорту пользоваться аппаратными функциями сетевого адаптера для выполнения таких операций, как расчет контрольных сумм пакетов и все вычисления, связанные с IP-безопасностью (IPSec). Аппаратное ускорение этих операций средствами сетевого адаптера повышает производительность системы, освобождая центральный процессор от выполнения этих задач.

• Функция Wake-On-LAN дает возможность сетевому адаптеру с соответствующей поддержкой выводить систему Windows из состояния с низким энергопотреблением при каких-либо событиях в сети. Сигнал пробуждения может быть инициирован сетевым адаптером при одном из следующих событий: подключении к несущей среде (например, подключении сетевого кабеля к адаптеру) и приеме специфичных для протокола последовательностей байтов (в случае адаптеров Ethernet — при получении волшебного пакета, т. е. сетевого пакета с 16 копиями Ethernet-адреса адаптера подряд).

• NDIS, ориентированная на логические соединения, позволяет драйверам NDIS управлять несущей средой, требующей логических соединений, например устройствами ATM (Asynchronous Transfer Mode). Интерфейсы, предоставляемые библиотекой NDIS драйверам NDIS для взаимодействия с сетевыми адаптерами, доступны через функции, вызовы которых транслируются непосредственно в вызовы соответствующих HAL функций.

ЭКСПЕРИМЕНТ: перечисление загруженных минипортов NDIS

Библиотека расширения отладчика ядра Ndiskd поддерживает команды !miniports и /miniport, которые позволяют с помощью отладчика ядра перечислять загруженные минипорт-драйверы (минипорты) и получать детальную информацию о каком-либо минипорте по заданному адресу блока минипорта (структуры данных, применяемой Windows для отслеживания минипортов). Ниже приведен пример использования команд !miniports и /miniport для вывода списка всех минипорт-драйверов, а также для исследования специфики минипорта, отвечающего за взаимодействие системы с PCI-адаптером Ethernet (заметьте, что WAN-минипорты работают с соединениями удаленного доступа).

Поле Flags исследуемого минипорта показывает, что он поддерживает несериализованные операции (DESERIALIZED), что несущая среда в данный момент активна (MEDIACONNECTED) и что он является минипортом NDIS 5 (NDIS_5_0). Кроме того, выводится информация, отражающая сопоставления состояний электропитания устройства и системы, а также список ресурсов шины, назначенных адаптеру диспетчером Plug and Play (Подробнее о соответствии состояний электропитания устройств и системы см. раздел «Диспетчер электропитания» главы 9.)

Разновидности минипорт-драйверов NDIS

Модель NDIS также поддерживает гибридные NDIS-драйверы транспорта TDI, называемые промежуточными драйверами NDIS (NDIS intermediate drivers). Они размещаются между транспортами TDI и драйверами NDIS. Драйверу NDIS промежуточный драйвер кажется транспортом TDI, а транспорту TDI — драйвером NDIS. Промежуточные драйверы NDIS видят весь сетевой трафик в системе, поскольку они расположены между драйверами протоколов и сетевыми драйверами. Программное обеспечение, предоставляющее сетевым адаптерам поддержку отказоустойчивости и балансировки нагрузки, например Microsoft Network Load Balancing Provider, основано на использовании промежуточных драйверов NDIS.

NDIS, ориентированная на логические соединения

Поддержка сетевого оборудования, ориентированного на логические соединения (например, ATM) в Windows является встроенной, и соответствующие стандарты учтены в сетевой архитектуре Windows. Драйверы NDIS, ориентированные на логические соединения, используют многие API, применяемые и стандартными драйверами NDIS, но посылают пакеты через установленные сетевые соединения, а не просто помещают их в сетевую среду.

Кроме поддержки минипорт-драйверов для сетевых сред, ориентированных на логические соединения, в NDIS 5 включены определения для драйверов, поддерживающих такие минипорт-драйверы.

• Диспетчеры вызовов (call managers) являются драйверами NDIS, которые предоставляют сервисы настройки и завершения вызовов для клиентов, ориентированных на логические соединения (см. ниже). Диспетчер вызовов использует ориентированный на логические соединения мини-порт, чтобы обмениваться сигнальными сообщениями с другими сетевыми компонентами (аппаратными или программными), например с коммутаторами или другими диспетчерами вызовов. Диспетчер вызовов поддерживает один или несколько сигнальных протоколов вроде ATM User-Network Interface (UNI) 3.1.

• Интегрированный Miniport Call Manager (MCM) представляет собой ми-нипорт-драйвер, ориентированный на логические соединения, который также предоставляет клиентам, требующим логических соединений, сервисы диспетчера вызовов. B сущности, MCM — это минипорт-драйвер NDIS со встроенным диспетчером вызовов.

• Ориентированный на логические соединения клиент использует сервисы настройки и завершения вызовов, предоставляемые диспетчером вызовов или MCM, а также передает и принимает обращения к сервисам минипорт-драйвера NDIS, ориентированного на логические соединения. Такой клиент может предоставлять собственные сервисы протокола более высоким уровням сетевого стека или реализовать уровень эмуляции для взаимодействия с унаследованными протоколами, не требующими логических соединений, и соответствующей несущей средой. Пример уровня эмуляции, реализуемой ориентированным на логические соединения клиентом, — LAN Emulation (LANE), которая скрывает от вышележащих протоколов особенности ориентированной на логические соединения ATM и эмулирует для них несущую среду, не требующую соединений (например, Ethernet).

Взаимосвязи между этими компонентами показаны на рис. 13–19.

ЭКСПЕРИМЕНТ: захват сетевых пакетов с помощью сетевого монитора

Windows Server поставляется с программой Network Monitor (Сетевой монитор), которая позволяет перехватывать пакеты, проходящие через один или несколько минипорт-драйверов NDIS, за счет установки промежуточного драйвера NDIS. Для использования Network Monitor нужно сначала установить Network Monitor Tools (Средства сетевого монитора). Для этого откройте апплет Add/Remove Programs (Установка и удаление программ) в Control Panel (Панель управления) и выберите Add/Remove Windows Components (Добавление и удаление компонентов Windows). Укажите строку Management And Monitoring Tools (Средства управления и наблюдения), щелкните кнопку Details (Состав), установите флажок в строке Network Monitor Tools (Средства сетевого монитора) и щелкните кнопку ОК. После установки Network Monitor (Сетевой монитор) можно запустить, выбрав одноименную команду из меню Administrative Tools (Администрирование).

Network Monitor может спросить, за каким сетевым соединением вы хотите наблюдать. Выбрав нужное соединение, можно начать мониторинг, щелкнув на панели инструментов кнопку Start Capture (Начать запись данных). Выполните несколько операций, генерирующих сетевую активность в отслеживаемом соединении. Увидев, что Network Monitor захватил пакеты, остановите мониторинг, щелкнув кнопку Stop And View Capture (Закончить запись и отобразить данные). B результате Network Monitor покажет захваченные данные.

Ha этой иллюстрации показаны пакеты SMB (CIFS), захваченные Network Monitor при обращении системы к удаленным файлам. Если вы дважды щелкнете какую-нибудь строку, Network Monitor переключится в режим отображения пакетов, в котором показывается содержимое различных заголовков в пакетах.

Network Monitor поддерживает и другие возможности, например захват триггеров и фильтров, что делает его мощным диагностическим инструментом, помогающим выявлять и устранять неполадки в сети.

Remote NDIS

До разработки Remote NDIS производитель, например, сетевого USB-устрой-ства должен был предоставлять драйвер, который создавал интерфейс с NDIS (в качестве минипорт-драйвера) и с WDM-драйвером шины USB рис. 13–20). Если производитель оборудования поддерживал другие шины, скажем, IEEE 1394, он должен был реализовать драйверы, создающие интерфейсы с каждым типом шины.

Рис. 13–20. Минипорт-драйвер NDIS для сетевого USB-устройства

Remote NDIS — спецификация для сетевых устройств на PnP-шинах ввода-вывода, допускающих динамическое подключение устройств, например USB, IEEE 1394 и Infiniband. Эта спецификация вообще избавляет производителя оборудования от необходимости писать минипорт-драйвер NDIS, так как в ней определены сообщения, независимые от конкретной шины, и механизм, с помощью которого сообщения передаются по различным шинам. B Remote NDIS включены сообщения для инициализации и сброса состояния устройства, передачи и приема пакетов, установки и опроса параметров устройства, а также для уведомления о состоянии канала передачи данных.

Архитектура Remote NDIS (рис. 13–21) построена на минипорт-драйвере NDIS от Microsoft, \Windows\System32\Drivers\Rndismp.sys, который транслирует NDIS-команды и передает их драйверу транспорта для шины, к которой подключено устройство. Эта архитектура позволяет использовать один минипорт-драйвер NDIS для всех драйверов Remote NDIS и один драйвер транспорта для каждой поддерживаемой шины.

B настоящее время Remote NDIS для USB-устройств поддерживается в Windows XP и Windows Server 2003; кроме того, соответствующие компоненты можно скачать с сайта Microsoft и установить в Windows 2000. Хотя Remote NDIS для устройств IEEE 1394 полностью определен, он пока не поддерживается в Windows.

Рис. 13–21. Архитектура Remote NDIS для сетевых USB-устройств

QoS

Без специальных мер IP-трафик в сети доставляется по принципу «первым пришел — первым обслужен». Приложения не могут контролировать приоритет своих сообщений, и их данные передаются неравномерно: иногда они получают широкую полосу пропускания и малые задержки, а в остальное время — узкую полосу пропускания и длительные задержки. Хотя такой уровень обслуживания в большинстве ситуаций вполне приемлем, все большее число сетевых приложений требует гарантированных уровней обслуживания, или гарантий качества обслуживания (Quality of Service, QoS). Примерами приложений, требующих хорошей сетевой производительности, могут служить видеоконференции, потоковая передача мультимедийной информации и программное обеспечение для планирования ресурсов предприятия (enterprise resource planning, ERP). QoS позволяет приложениям указывать минимальную ширину полосы пропускания и максимальные задержки, которые могут быть удовлетворены только в том случае, если все сетевое программно-аппаратное обеспечение на пути между отправителем и получателем поддерживает стандарты QoS, например IEEE 802.1p — промышленный стандарт, который определяет формат пакетов QoS и реакцию на их получение устройств второго сетевого уровня (коммутаторов и сетевых адаптеров).

Поддержка QoS в Windows основана на наборе Winsock-функций, определенных Microsoft и позволяющих приложениям запрашивать QoS для трафика через свои сокеты Winsock, а также на API управления трафиком (TC API), который позволяет административным приложениям более точно контролировать трафик через сети.

Центральное место в реализации QoS в Windows занимает протокол RSVP (Resource Reservation Setup Protocol), представляющий собой Windows-сервис (\Windows\System32\Rsvp.exe), как показано на рис. 13–22. Провайдер службы RSVP (\Windows\System32\Rsvpsp.dll) передает QoS-запросы приложений через RPC службе RSVP Ta в свою очередь контролирует сетевой трафик с помощью TC API. TC API, реализованный в \Windows\System32\Traffic.dll, посылает команды управления вводом-выводом драйверу GPC (Generic Packet Classifier) (\Windows\System32\Drivers\Msgpc.sys). Дpaйвep GPC — втесном взаимодействии с планировщиком пакетов QoS (промежуточным драйвером NDIS) (\Windows\System32\Drivers\Psched.sys) — контролирует поток пакетов с компьютера в сеть, гарантируя уровни QoS, обещанные конкретным приложениям. При этом он вставляет в пакеты соответствующие заголовки QoS.

ПРИМЕЧАНИЕ B Windows XP и Windows Server 2003 служба RSVP по-прежнему работает, но действует лишь как посредник между приложениями и компонентами, управляющими трафиком.

Привязка

Последний фрагмент головоломки под названием «сетевая архитектура Windows» — способ, посредством которого сетевые компоненты, расположенные на различных уровнях (сетевых API, драйверов транспортов TDI, драйверов NDIS), находят друг друга. Процесс соединения уровней называется привязкой (binding). Вы сами были свидетелем привязки, если хоть раз изменяли конфигурацию сети, добавляя или удаляя компоненты в окне свойств сетевого соединения.

Устанавливая сетевой компонент, вы должны предоставить его INF-файл (INF-файлы описаны в главе 9). Этот файл содержит инструкции, которым должны следовать API-функции установки, чтобы установить и сконфигурировать компонент, учитывая его зависимости от других компонентов. Разработчик может указать зависимости для своего компонента, что позволит SCM загружать его в корректной очередности и только тогда, когда все компоненты, от которых он зависит, уже присутствуют в системе (о SCM см. главу 4). Привязки определяются механизмом привязки (bind engine) на основе информации из INF-файла компонента, что позволяют соединить его с другими компонентами, расположенными на различных уровнях. Эти соединения указывают, какие компоненты нижележащего уровня могут быть использованы сетевым компонентом данного уровня.

Так, служба рабочей станции (редиректор) автоматически привязывается к протоколам TCP/IP и NWLink. Порядок привязки, который можно увидеть на вкладке Adapters And Bindings (Адаптеры и привязки) диалогового OKHaAdvancedSettings (Дополнительные параметры) (рис. 13–23), определяет приоритет привязки. (O том, как открыть это диалоговое окно, см. раздел «Поддержка нескольких редиректоров» ранее в этой главе.) Получив запрос на доступ к удаленному файлу, редиректор выдает запрос обоим драйверам протоколов. После того как редиректору приходит ответ, он дополнительно ждет ответы от любых драйверов протоколов с более высоким приоритетом. И только тогда редиректор возвращает результат вызывающей программе. Поэтому, присвоив высокий приоритет привязкам, которые дают максимальную производительность или применимы к большинству компьютеров в сети, можно добиться определенного выигрыша.

Рис. 13–23. Редактирование привязок в диалоговом окне Advanced Settings

Информация о привязках компонента содержится в параметре Bind подраздела Linkage того раздела реестра, в котором хранится конфигурация данного сетевого компонента. Например, информацию о привязках службы рабочей станции можно найти в HKLM\SYSTEM\CurrentControlSet\Services\ LanmanWorkstation\Linkage\Bind.

Многоуровневые сетевые сервисы

Windows включает сетевые сервисы, построенные на основе API и компонентов, представленных в этой главе. Описание возможностей и внутренних деталей их реализации выходит за рамки этой книги, но мы приведем здесь краткий обзор по удаленному доступу, службе каталогов Active Directory, Network Load Balancing, службе репликации файлов (File Replication Service, FRS) и распределенной файловой системе (Distributed File System, DFS).

Удаленный доступ

Windows Server с установленной службой Routing and Remote Access Service (Служба маршрутизации и удаленного доступа) позволяет клиентам удаленного доступа подключаться к серверам удаленного доступа и обращаться к таким сетевым ресурсам, как файлы, принтеры и службы, создавая иллюзию физического подключения к серверу удаленного доступа через локальную сеть. Windows поддерживает два типа удаленного доступа.

• Удаленный доступ через телефонную линию (dial-up remote access) Используется клиентом при подключении к серверу удаленного доступа через телефонную линию или иную телекоммуникационную инфраструктуру Телекоммуникационная несущая среда используется для создания временного физического или виртуального соединения между клиентом и сервером.

• Удаленный доступ через виртуальную частную сеть (virtual private network, VPN) Позволяет клиентам VPN устанавливать с сервером виртуальное соединение типа «точка-точка» через IP-сеть, например Интернет.

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

Active Directory

Active Directory — реализация службы каталогов LDAP (Lightweight Directory Access Protocol) в Windows. Active Directory основана на базе данных, в которой хранятся объекты, представляющие ресурсы, определенные приложениями в сети Windows. Например, в Active Directory хранится структура и список членов домена Windows, включая информацию об учетных записях и паролях пользователей.

Классы объектов и атрибуты, определяющие свойства объектов, задаются схемой (schema). Иерархическая организация объектов в схеме Active Directory напоминает логическую организацию реестра, где объекты-контейнеры могут хранить другие объекты, включая контейнеры.

Active Directory поддерживает несколько API, используемых клиентами для обращения к объектам в базе данных Active Directory.

• LDAP C API Предназначен для программ на С/С++ и использует сетевой протокол LDAP. Приложения, написанные на С/С++, могут работать с этим API напрямую, а приложения, написанные на других языках, — через транслирующие уровни.

• ADSI (Active Directory Service Interfaces) СОМ-интерфейс Active Directory, реализованный поверх LDAP и абстрагирующий от деталей программирования для LDAP. ADSI поддерживает несколько языков, в том числе Microsoft Visual Basic, C и Microsoft Visual C++. ADSI также доступен приложениям Windows Script Host (Сервер сценариев Windows).

• MAPI (Messaging API) Поддерживается для совместимости с клиентами Microsoft Exchange и клиентскими приложениями Outlook Address Book.

• Security Account Manager (SAM) API Базируется на сервисах Active Directory и предоставляет интерфейс пакетам аутентификации MSVlO (\Windows\System32\Msvl_O.dll) и Kerberos (\Windows\System32\Kdcsvc.dll).

• Сетевые API Windows NT 4 (Net API) Используются клиентами Windows NT 4 для доступа к Active Directory через SAM.

• NTDS API Применяется для просмотра SID и GUID в Active Directory (в основном через DsCrackNames), а также для управления каталогом и его репликацией. Несколько сторонних разработчиков написали приложения, позволяющие вести мониторинг Active Directory через этот API. Active Directory реализована в виде файла базы данных (по умолчанию — в \Windows\Ntds\Ntds.dit), реплицируемой между контроллерами домена. Этой базой данных управляет служба каталогов Active Directory, которая является Windows-сервисом, выполняемым в процессе LSASS; при этом она использует DLL, реализующие структуру базы данных на диске и предоставляющие механизмы обновления на основе транзакций для поддержания целостности базы данных. B Windows 2000 хранилище базы данных Active Directory реализовано на основе ядра Extensible Storage Engine (ESE), применяемого в Microsoft Exchange Server 5.5, a в Windows 2003 Server — на основе ядра ESE, используемого в Microsoft Exchange Server 2000. Архитектура Active Directory показана на рис. 13–24.

Network Load Balancing

Как мы уже говорили в этой главе, в основе компонента Network Load Balancing (Балансировка нагрузки сети), который входит в Windows Advanced Server, лежит технология промежуточных драйверов NDIS. Network Load Balancing допускает создание кластера, включающего до 32 компьютеров, которые в терминологии Network Load Balancing называются узлами кластера (cluster hosts). Кластер поддерживает единый виртуальный 1Р-адрес, публикуемый клиентам, и клиентские запросы поступают ко всем компьютерам кластера. Однако на запрос отвечает только один узел кластера. Драйверы NDIS компонента Network Load Balancing эффективно разделяют клиентское пространство между доступными узлами кластера по аналогии с распределенной обработкой. При таком подходе каждый узел обрабатывает свою порцию клиентских запросов, причем каждый клиентский запрос обрабатывается одним — и только одним — узлом. Если входящий в состав кластера узел определяет, что именно он должен обработать клиентский запрос, то этот узел позволяет запросу пройти до уровня драйвера TCP/IP и в конце концов достичь серверного приложения. Если на узле кластера происходит авария, остальные узлы кластера распознают, что этот узел больше не способен обрабатывать запросы, и перераспределяют поступающие клиентские запросы между собой; при этом клиентские запросы отключенному узлу больше не посылаются. K кластеру можно подключить новый узел на замену потерпевшему аварию, и он автоматически примет участие в обработке клиентских запросов.

Network Load Balancing не является универсальным кластерным решением, поскольку серверные приложения, с которыми взаимодействуют клиенты, должны обладать определенными характеристиками. Во-первых, они должны поддерживать TCP/IP, а во вторых, уметь обрабатывать клиентские запросы на любой системе в кластере Network Load Balancing. Второе требование, как правило, означает, что приложения, у которых для обслуживания клиентских запросов должен быть доступ к общему состоянию (shared state), обязаны сами управлять этим состоянием. B Network Load Balancing не входят сервисы автоматического распределения общего состояния между узлами кластера. Приложения, идеально подходящие для Network Load Balancing, — Web-сервер со статичным информационным наполнением (контентом), Windows Media Server и Terminal Services (Службы терминалов). Пример работы Network Load Balancing показан на рис. 13–25.

Служба репликации файлов

Служба репликации файлов (File Replication Service, FRS) входит в системы Windows Server. Она предназначена в основном для репликации содержимого каталога \SYSVOL контроллера домена (в этом месте контроллеры доменов Windows хранят сценарии регистрации и групповые политики). Кроме того, FRS позволяет реплицировать общие ресурсы DFS (Distributed File System) между системами. FRS поддерживает распределенную репликацию с несколькими хозяевами (distributed multimaster replication), благодаря чему репликацию может проводить любой сервер. Когда реплицируемый файл или каталог изменяется, эти изменения распространяются на другие контроллеры домена.

Фундаментальное понятие в FRS — набор репликации (replica set), представляющий собой дерево каталогов, которое реплицируется между двумя или более системами по определенной топологии и расписанию, заданному администратором. Реплицированы могут быть только каталоги на томах NTFS, поскольку FRS использует журнал изменений NTFS для определения модификаций в файлах и каталогах, включенных в набор репликации. Поскольку FRS обеспечивает репликацию с несколькими хозяевами, теоретически она может поддерживать сотни и даже тысячи систем в наборе репликации, а топология соединения соответствующих компьютеров может быть совершенно произвольной (кольцо, звезда, сетка и др.). Кроме того, компьютеры могут участвовать в нескольких наборах репликации.

FRS реализована в виде Windows-сервиса (\Windows\System32\Ntfrs.exe), который использует аутентифицируемый RPC для взаимодействия со своими экземплярами, работающими на других компьютерах. Кроме того, поскольку Active Directory располагает собственными средствами репликации, FRS использует API-функции Active Directory для выборки конфигурационной информации из домена Active Directory.

DFS

DFS (Distributed File System) — сервис поверх службы рабочей станции, соединяющий отдельные файловые ресурсы в единое пространство имен. DFS обеспечивает клиентам прозрачный доступ к файловым ресурсам независимо от того, где находятся эти ресурсы — на локальном или удаленных компьютерах. Корнем пространства имен DFS должен быть файловый ресурс, определенный на компьютере с Windows Server.

B дополнение к унифицированному пространству имен сетевых ресурсов DFS дает и другие преимущества при использовании наборов репликации DFS. Администратор может создать набор репликации DFS минимум из двух сетевых ресурсов и использовать механизм репликации вроде FRS для копирования данных между ресурсами, входящими в набор репликации, и тем самым обеспечить синхронизацию их содержимого. DFS поддерживает несколько видов балансировки нагрузки, упорядочивая и/или выбирая сетевые ресурсы, входящие в набор репликации, при обращении клиента к данным из этого набора. DFS также обеспечивает высокую доступность данных, перенаправляя запросы на другие сетевые ресурсы из набора репликации, если какой-то из сетевых ресурсов временно недоступен.

Компоненты, образующие архитектуру DFS, показаны на рис. 13–26. Реализация DFS на серверной стороне включает Windows-сервис (\Windows\ System32\Dfssvc.exe) и драйвер устройства (\Windows\System32\Drivers\ Dfs.sys). Служба DFS отвечает за экспорт интерфейсов управления топологией DFS и поддержку топологии DFS либо в реестре (в отсутствие Active Directory), либо в Active Directory. Драйвер DFS принимает клиентский запрос и переадресует его системе, на которой находится запрошенный файл.

Ha клиентской стороне поддержка DFS реализована в драйвере MUP (о нем мы уже рассказывали) и использует редиректор CIFS для взаимодействия с серверами DFS на внутреннем уровне. Провайдер клиента DFS реализован в \Windows\System32\Ntlanman.dll. Когда клиент выдает запрос на ввод-вывод для файла в пространстве имен DFS, драйвер MUP на клиентской стороне взаимодействует с сервером, на котором находится этот файл, через подходящий редиректор.

Резюме

Сетевая архитектура Windows предоставляет гибкую инфраструктуру сетевым API, драйверам протоколов и сетевых адаптеров. Эта архитектура использует преимущества многоуровневого ввода-вывода, обеспечивая расширяемость сетевой поддержки по мере развития компьютерных сетей. При появлении нового протокола разработчики смогут создать транспорт TDI, реализующий этот протокол в Windows. Аналогичным образом новые API смогут взаимодействовать с существующими драйверами протоколов Windows. Наконец, набор сетевых API, реализованных в Windows, позволяет разработчикам сетевых приложений выбирать подходящие им реализации, поддерживающие разные модели программирования и протоколы.

 

ГЛABA 14 Анализ аварийного дампа

Почти каждый пользователь Windows слышал о так называемом «синем экране смерти» (blue screen of death, BSOD) или даже видел его. Этим зловещим термином называют экран с синим фоном, показываемый при крахе или остановке Windows из-за катастрофического сбоя или внутренней ситуации, из-за которой стала невозможной дальнейшая работа системы.

B этой главе мы рассмотрим основные причины краха Windows, опишем информацию, выводимую на «синем экране» и расскажем о различных параметрах конфигурации, управляющих созданием аварийного дампа fcrash dump) — копии системной памяти на момент краха, которая может помочь определить, какой именно компонент вызвал крах. B цели данного раздела не входит детальное рассмотрение способов выявления и устранения проблем с помощью анализа аварийного дампа Windows. Тем не менее в этом разделе показывается, как, проанализировав аварийный дамп, идентифицировать некорректно работающий драйвер или компонент. Для базового анализа аварийного дампа требуется минимум усилий и несколько минут времени. Анализ дампа стоит проводить, даже если проблемный драйвер удается выявить только с пятой или десятой попытки: успешно выполненный анализ позволит избежать потерь данных и простоя системы.

Почему происходит крах Windows?

Крах Windows (остановка системы и вывод «синего экрана») может быть вызван следующими причинами:

• необработанным исключением, вызванным драйвером устройства или системной функцией режима ядра, например из-за нарушения доступа к памяти (при попытке записи на страницу с атрибутом «только для чтения» или чтения по еще не спроецированному и, следовательно, недопустимому адресу);

• вызовом процедуры ядра, результатом которой является перераспределение процессорного времени из-за, например, ожидания на занятом объекте диспетчера ядра при IRQL уровня «DPC/dispatch» или выше (об IRQL см. главу 3);

• обращением к данным на выгруженной из памяти странице при IRQL уровня «DPC/dispatch» или выше (что требует от диспетчера памяти ждать операции ввода-вывода, а это, как уже говорилось, невозможно на таких уровнях IRQL, поскольку требует перераспределения процессорного времени);

• явным вызовом краха системы драйвером устройства или системной функцией (через функцию KeBugCheckEx) при обнаружении поврежденных внутренних данных или в ситуации, когда продолжение работы системы грозит таким повреждением;

• аппаратной ошибкой, например ошибкой аппаратного контроля или появлением немаскируемого прерывания (Non-Maskable Interrupt, NMI). B Microsoft проанализировали аварийные дампы, отправляемые пользователями Windows XP на сайт Microsoft Online Crash Analysis (OCA) (о нем еще пойдет речь в этой главе), и обнаружили, что причины краха систем распределяются, как показано на диаграмме на рис. 14-1 (по состоянию на апрель 2004 года).

Когда драйвер устройства или компонент режима ядра вызывает необрабатываемое исключение, перед Windows встает трудная дилемма. Какая-то часть операционной системы, имеющая право доступа к любым аппаратным устройствам и любому участку памяти, сделала нечто такое, чего делать нельзя.

Ho почему при этом обязательно должен произойти крах Windows? Почему бы не проигнорировать это исключение и не позволить драйверам работать дальше, как ни в чем не бывало? Ведь не исключено, что ошибка носила локальный характер и соответствующий компонент как-нибудь сумеет после нее восстановиться. Ho гораздо вероятнее, что обнаруженное исключение связано с более серьезными проблемами, например с повреждением памяти или со сбоями в работе оборудования. Тогда дальнейшее функционирование системы скорее всего приведет к еще большему числу исключений и порче данных на дисках и других периферийных устройствах, а это слишком рискованно.

«Синий экран»

Независимо от причины реальный крах системы вызывается функцией Ke-BugCbeckEx (документирована в Windows DDK). Она принимает так называемый стоп-код (stop code), или контрольный код ошибки (bug check code), и четыре параметра, интерпретируемые с учетом стоп-кода. KeBugCbeckEx маскирует все прерывания на всех процессорах системы, а затем переключает видеоадаптер в графический режим VGA с низким разрешением (поддерживаемый всеми видеокартами, совместимыми с Windows) и выводит на синем фоне значение стоп-кода и несколько строк текста с рекомендациями относительно дальнейших действий. Наконец, KeBugCbeckEx вызывает все зарегистрированные (с помощью функции KeRegisterBugCbeckCallback) функции обратного вызова драйверов устройств при ошибке (device driver bug check callbacks), чтобы они могли остановить свои устройства. (Системные структуры данных могут быть настолько серьезно повреждены, что «синий экран» может и не появиться.) Образец «синего экрана» Windows XP показан на рис. 14-2.

ПРИМЕЧАНИЕ B Windows XP Service Pack 1 (или выше) и в Windows Server 2003 введена функция KeRegisterBugCheckReasonCallback, позволяющая драйверам устройств добавить данные в аварийный дамп или вывести информацию аварийного дампа на альтернативное устройство.

B Windows 2000 KeBugCheckEx выводит текстовое представление стопкода, его числовое значение и четыре параметра вверху «синего экрана», но в Windows XP и Windows Server 2003 числовое значение и параметры показываются внизу «синего экрана».

B первой строке выводится стоп-код и значения четырех дополнительных параметров, переданных в KeBugCheckEx. Строка вверху экрана представляет собой текстовый эквивалент числового идентификатора стоп-кода. B примере на рис. 14-2 стоп-код 0x000000D1 соответствует IRQL_NOT_ LESS_OR_ EQUAL. Если параметр содержит адрес части операционной системы или кода драйвера устройства (как на рис. 14-2), Windows выводит базовый адрес соответствующего модуля, дату и имя файла драйвера. Одной этой информации может оказаться достаточно для идентификации сбойного компонента.

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

Информацию, необходимую для интерпретации стоп-кодов, можно найти в разделе «Bug Checks (Blue Screens)» справочного файла Windows Debugging Tools. (Сведения о Windows Debugging Tools см. в главе 1.) Кроме того, можно поискать стоп-код и имя проблемного устройства или приложения в Microsoft Knowledge Base (http://supportmicrosoft.com). B ней можно найти информацию о способах исправления ошибки, об обновлениях или сервисных пакетах, решающих проблему, с которой вы столкнулись. Файл Bug-codes.h в Windows DDK содержит полный список из примерно 150 стоп-кодов с детальным описанием некоторых из них.

«Синие экраны» часто возникают после установки нового программного обеспечения или оборудования. Если вы видите «синий экран» сразу после установки нового драйвера на раннем этапе перезагрузки, то можете вернуть прежнюю конфигурацию системы, нажав клавишу F8 и выбрав из дополнительного загрузочного меню команду Last Known Good Configuration (Последняя удачная конфигурация). Тогда Windows использует копию раздела реестра, где были зарегистрированы драйверы устройств (HKLM\SYSTEM\ CurrentControlSet\Services) при последней успешной загрузке (до установки нового драйвера). Последней удачной конфигурацией считается последняя конфигурация, в которой успешно завершилась загрузка всех сервисов и драйверов и был выполнен минимум один успешный вход в систему. (O последней удачной конфигурации более подробно рассказывается в главе 5.)

Если это не помогает и вы по-прежнему видите «синие экраны», то самый очевидный подход — удалить компоненты, установленные перед появлением первого «синего экрана». Если после установки уже прошло некоторое время или вы одновременно добавили несколько устройств либо драйверов, обратите внимание на имена драйверов, указываемые в каких-либо параметрах на «синем экране». Если там есть ссылка на недавно установленные компоненты (например, Scsiport.sys в случае установки нового SCSI-диска), причина сбоя скорее всего связана именно с ними.

Имена многих драйверов весьма загадочны, но вы можете выяснить, какие устройства или программные компоненты соответствуют данному имени. Для этого просмотрите раздел реестра HKLM\SYSTEM\CurrentControlSet\ Services, где Windows хранит регистрационную информацию для каждого драйвера в системе, и попробуйте найти имя сервиса и сопоставленный с ним драйвер устройства. Описание найденного драйвера содержится в параметрах DisplayName и Description, здесь также описывается предназначение некоторых драйверов. Так, строка «Virus Scanner», обнаруженная в DisplayName, говорит о том, что драйвер является частью антивирусной программы. Список драйверов также можно вывести с помощью утилиты System Information (Сведения о системе): раскройте в ней узел Software Environment (Программная среда) и выберите System Drivers (Системные драйверы).

Однако чаще всего информации, сообщаемой стоп-кодом и сопоставленными с ним параметрами, недостаточно для устранения сбоя, приводящего к краху системы. Так, чтобы выяснить точное имя драйвера или системного компонента, вызывающего крах, может понадобиться анализ стека вызовов режима ядра. Поскольку в Windows после краха системы по умолчанию следует перезагрузка и у вас вряд ли будет время для изучения информации, представленной на «синем экране», Windows пытается записывать информацию о крахе системы на диск для последующего анализа. Эта информация помещается в файлы аварийного дампа.

Файлы аварийного дампа

По умолчанию все Windows-системы настраиваются на запись информации о состоянии системы на момент краха. Соответствующие настройки можно увидеть так откройте System (Система) в Control Panel (Панель управления), в окне свойств системы перейдите на вкладку Advanced (Дополнительно) и щелкните кнопку Startup And Recovery (Загрузка и восстановление). Ha рис. 14-3 показаны настройки по умолчанию для системы Windows XP Professional.

При крахе системы может быть зарегистрировано три уровня информации.

• Complete memory dump (Полный дамп памяти) Полный дамп памяти представляет собой все содержимое физической памяти на момент краха. Для такого дампа нужно, чтобы размер страничного файла был равен, как минимум, объему физической памяти плюс 1 Мб (для заголовка). Этот параметр используется реже всего, так как в системах с большим объемом памяти страничный файл будет слишком велик. Windows NT 4 поддерживает только этот тип файлов аварийного дампа. Кроме того, этот параметр используется по умолчанию в системах Windows Server.

• Kernel memory dump (Дамп памяти ядра) Этот вариант дампа включает лишь страницы (как для чтения, так и для записи) режима ядра, находящиеся в физической памяти на момент краха. Страницы, принадлежащие пользовательским процессам, не включаются. Поскольку только код режима ядра может напрямую вызывать крах Windows, содержимое страниц пользовательских процессов обычно ничего не дает для понимания причин краха. Кроме того, все структуры данных, используемые при анализе аварийного дампа, — список выполняемых процессов, стек текущего потока и список загруженных драйверов — хранятся в неподкачиваемой памяти, содержимое которой запоминается в дампе памяти ядра. Заранее предсказать объем дампа памяти ядра нельзя, поскольку он зависит от объема памяти ядра, выделенной операционной системой и драйверами.

• Small memory dump (Малый дамп памяти) Размер этого дампа (вариант по умолчанию в системах Windows Professional) составляет 64 Кб (128 Кб в 64-битньгх системах). Такой дамп еще называют минидампом (minidump) или минимальным дампом (triage dump). Он включает в себя стоп-код с параметрами, список загруженных драйверов устройств, структуры данных, описывающие текущие процесс и поток (PROCESS и ETHREAD, которые рассматриваются в главе 6), а также стек ядра доя вызвавшего крах потока. Полный дамп памяти является надмножеством двух других дампов, но у него есть недостаток: его размер зависит от объема физической памяти системы и, следовательно, он может оказаться слишком большим. Мощные серверные системы, оснащенные несколькими гигабайтами памяти, — не такая уж редкость. Записываемые на них файлы полного аварийного дампа будут слишком велики для закачивания на FTP-сервер или прожигания на CD. Поскольку в большинстве случаев код и данные пользовательского режима не используются при анализе аварийных дампов (ведь причиной краха являются проблемы, связанные с памятью ядра, системные структуры данных также содержатся в памяти ядра), большая часть данных, сохраненных в полном дампе памяти, не нужна для анализа и впустую увеличивает размер файла дампа. Наконец, еще один недостаток в том, что размер страничного файла на загрузочном томе (содержащем каталог \Windows) должен быть равен объему физической памяти системы плюс 1 Мб. Поскольку необходимость в страничном файле, как правило, уменьшается с ростом объема физической памяти, это требование означает, что страничный файл будет неоправданно большим. Поэтому приходится признать, что лучше использовать малый дамп памяти или дамп памяти ядра.

Преимущество минидампа — его небольшой размер, благодаря которому, например, удобно передавать дамп по электронной почте. При каждом крахе в каталог \Windows\Minidump записывается файл с уникальным именем, начинающимся со строки «Mini», за которой идут дата и порядковый номер (например, Mini082604-01.dmp). Недостаток минидампов в том, что доя их анализа нужны именно те образы, которые использовались системой, сгенерировавшей дамп. (Даже для самого простого анализа, как минимум, необходима копия соответствующего Ntoskrnl.exe.) Это может стать проблемой, если вы анализируете дамп не на той системе, где он был создан. Однако на сервере символов Microsoft есть образы (и символы) для систем Windows XP и более поздних версий, поэтому можно задать в отладчике путь к образу, указывающий на сервер символов, и отладчик автоматически скачает нужные образы. (Конечно, на сервере символов Microsoft нет образов устанавливаемых вами драйверов сторонних производителей.)

Более существенный недостаток — такой дамп содержит ограниченное количество данных, что может помешать эффективному анализу. C минидампами можно работать, даже если вы настроили систему на генерацию дампа памяти ядра или полного дампа, — просто откройте более объемный дамп в Windbg и извлеките минидамп командой .dump /т. Заметьте: в Windows XP и Windows Server 2003 минидамп автоматически создается, даже если система настроена на генерацию полного дампа памяти или дампа памяти ядра.

ПРИМЕЧАНИЕ Выполнив команду. dump в Livekd, можно сгенерировать образ памяти работающей системы, чтобы, не останавливая систему, получить дамп для анализа в автономном режиме. Такой подход полезен, когда в системе проявляются какие-то проблемы, но она продолжает обслуживать клиентов и вы хотели бы устранить проблемы, не прерывая обслуживание. Полученный в результате дамп не обязательно будет полностью корректным, так как содержимое различных областей памяти извлекается в разные моменты времени, но может содержать информацию, полезную для анализа.

Золотой серединой является дамп памяти ядра. Он содержит всю физическую память режима ядра, и, следовательно, позволяет вести анализ на том же уровне, что и полный дамп памяти, но не содержит код и данные пользовательского режима, обычно не относящиеся к проблеме, и поэтому имеет значительно меньший размер. Так, в системе с 256 Мб памяти под управлением Windows XP дамп памяти ядра занимает 34 Мб, а в системе с Windows XP и 1,5 Гб памяти этот дамп требует 72 Мб.

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

Таким образом, может оказаться, что в момент краха системы страничный файл будет слишком мал для того, чтобы вместить дамп ядра. Если вы хотите узнать размер дампа ядра для своей системы, вызовите крах системы вручную: сконфигурируйте систему так, чтобы можно было вручную вызывать ее крах с консоли, или воспользуйтесь программой Notmyfault. (B этой главе описаны оба подхода.) После перезагрузки вы сможете проверить, сгенерирован ли дамп памяти ядра, и по его размеру оценить, каким должен быть размер страничного файла для вашего загрузочного тома. Для единообразия можно задавать для 32-разрядных систем размер страничного файла 2 Гб плюс 1 Мб, поскольку 2 Гб — максимальный размер адресного пространства режима ядра.

Наконец, даже если система в случае краха успешно записывает аварийный дамп в страничный файл, нужно, чтобы на диске хватало места для извлечения файла дампа. Если места не хватит, аварийный дамп пропадет, поскольку используемое им пространство страничного файла высвободится и будет перезаписано, когда система начнет использовать страничный файл. Если на загрузочном томе недостаточно места для сохранения файла memory.dmp, можно задать путь на другом жестком диске в диалоговом окне, показанном на рис. 14-3.

Генерация аварийного дампа

При загрузке система получает параметры аварийного дампа из раздела реестра HKLM\System\CurrentControlSet\Control\CrashControl. Если задана генерация дампа, система создает копию минипорт-драйвера диска (disk miniport driver), используемую для записи загрузочного тома в память и присваивает ей то же имя, что и у минипорта, но с префиксом «dump». Кроме того, система подсчитывает и сохраняет контрольную сумму для компонентов, используемых при записи аварийного дампа: скопированного минипорт драйвера диска, функций диспетчера ввода-вывода, записывающих дамп, и карты области, в которой располагается страничный файл на загрузочном томе. Когда вызывается функция KeBugCheckEx, она заново пересчитывает контрольную сумму и сравнивает новую контрольную сумму с полученной при загрузке. Если они не совпадают, функция не записывает аварийный дамп, так как это может привести к сбою диска или повреждению данных на диске. Если контрольные суммы совпали, KeBugCheckEx записывает информацию дампа прямо в секторы диска, занимаемые страничным файлом, минуя драйвер файловой системы (который, возможно, поврежден или даже является причиной краха).

Когда SMSS в процессе загрузки активизирует постраничную подкачку, система проверяет, не содержится ли в страничном файле на загрузочном томе аварийный дамп, и защищает ту часть страничного файла, которая отведена под дамп. B результате на раннем этапе загрузки часть страничного файла или весь этот файл выводится из использования, что может вызвать системные уведомления о нехватке виртуальной памяти, однако это лишь временное явление. При дальнейшей загрузке Winlogon определяет, содержится ли дамп в страничном файле, вызывая недокументированную API-функцию NtQuerySystemInformation. Если дамп есть, запускается процесс Savedump (\Windows\System32\Savedump.exe), который извлекает аварийный дамп из страничного файла и записывает его в заданное место. Эти операции показаны на рис. 14-4.

Windows Error Reporting

Как уже говорилось в главе 3, в Windows XP и Windows Server 2003 имеется механизм Windows Error Reporting, позволяющий автоматически передавать данные о сбоях процессов и системы на анализ в Microsoft (или на внутренний сервер отчетов об ошибках). По умолчанию этот механизм включен. Ha его работу можно повлиять, изменив поведение процесса Savedump, который выполняет следующую дополнительную операцию: при перезагрузке после краха проверяет, настроена ли система на отправку аварийного дампа на анализ в Microsoft (или на закрытый сервер). Ha рис. 14-5 показано диалоговое окно Error Reporting (Отчет об ошибках), которое можно открыть с вкладки Advanced (Дополнительно) апплета System (Система) панели управления. B этом диалоговом окне можно настроить параметры системных отчетов об ошибках, хранящиеся в разделе реестра HKLM\Software\ Microsoft\PCHealth\ErrorReporting.

Рис. 14-5. Диалоговое окно настройки Error Reporting

После перезагрузки, вызванной крахом, Savedump проверяет несколько параметров, содержащихся в разделе ErrorReporting: Showui, DoReport и IncludeKernelFaults. Если все они имеют значение true, Savedump выполняет следующие операции по подготовке отчета о крахе системы к отправке на сайт Microsoft Online Crash Analysis (OCA) (или на внутренний сервер отчетов об ошибках, если это задано в настройках).

1. Если сгенерированный дамп не является минидампом, извлекает из файла дампа минидамп и записывает его в каталог по умолчанию — \Windows\ Minidumps.

2. Записывает имя файла минидампа в HKLM\Software\Microsoft\PCHealth\ ErrorReporting\KernelFaults.

3. Добавляет команду запуска утилиты Dumprep (\Windows\System32\Dump-rep.exe) в раздел HKLM\Software\Microsoft\Windows\CurrentVersion\Run, чтобы Dumprep запустилась при первом входе пользователя в систему.

Анализ аварийных дампов через Интернет

Когда запускается утилита Dumprep (в результате того, что Savedump добавила в реестр соответствующее значение), эта утилита проверяет те же три параметра, что и Savedump, чтобы определить, должна ли система отправить отчет об ошибке после перезагрузки, вызванной крахом. Если должна, Dumprep генерирует XML-файл, содержащий базовое описание системы, в том числе версию операционной системы, список драйверов, установленных на компьютере, и список драйверов Plug and Play, загруженных в момент краха. Затем Dumprep выводит диалоговое окно, показанное на рис. 14-6, запрашивая у пользователя, нужно ли отправить в Microsoft отчет об ошибке. Если пользователь указал, что нужно, и это не противоречит групповым политикам, Dumprep отправляет XML-файл и минидамп на сайт http://wat son.microsoft.com, который пересылает данные на серверную ферму, где отчеты автоматически анализируются (об этом см. следующий раздел). Через групповые политики администраторы могут настроить свои системы так, чтобы данные об ошибках направлялись во внутренний сетевой каталог, предназначенный для сбора данных об ошибках. B дальнейшем эти данные можно обрабатывать с помощью Microsoft Corporate Error Reporting (CER) Toolkit, доступного только избранным клиентам Microsoft Software Assurance (информацию см. по ссылке http://www.microsoft.com/resources/satech/cer).

Рис. 14-6. Диалоговое окно, предлагающее отправить отчет об ошибке

Ферма серверов автоматического анализа использует тот же механизм, что и разработанные Microsoft отладчики ядра, в которые вы можете загрузить аварийный дамп (вскоре мы о них расскажем). При анализе генерируется так называемый идентификатор типа (bucket ID) — сигнатура, идентифицирующая определенный тип краха. Ферма серверов выполняет запрос к базе данных, пытаясь по идентификатору типа найти решение проблемы, вызвавшей крах, и отправляет утилите Dumprep URL со ссылкой на сайт OCA (http://oca.microsofi.com). Dumprep запускает Web-браузер, чтобы открыть страницу сайта OCA с предварительными результатами анализа дампа. Если решение проблемы найдено, на странице выводятся инструкции о том, где получить критическое исправление, сервисный пакет или обновление стороннего драйвера; в ином случае предоставляется возможность получать информацию о ходе анализе дампа по электронной почте.

Если у организации нет доступа к Интернету или она не собирается автоматически отправлять аварийные дампы в Microsoft, то через групповые политики можно указать, что данные об ошибках должны храниться во внутреннем сетевом каталоге; в дальнейшем их можно будет обрабатывать с помощью Microsoft CER Toolkit, упоминавшегося выше.

Базовый анализ аварийных дампов

Если при анализе, выполненном ОСА, не удалось найти решение проблемы или если вы не сумели отправить аварийный дамп на сайт OCA (например, если этот дамп сгенерирован Windows 2000, не поддерживающей ОСА), то вы можете самостоятельно проанализировать дамп. Как уже говорилось, когда вы загружаете аварийный дамп в Windbg или Kd, эти отладчики ядра применяют тот же механизм анализа, что и ОСА. Иногда даже базового анализа достаточно для выявления проблемы. Таким образом, если вам повезет, вы найдете решение проблемы путем автоматического анализа аварийного дампа. Ho даже если и не повезет, существуют простые методики выявления причин краха.

B этом разделе поясняется, как выполнить базовый анализ аварийного дампа, затем даются рекомендации, как с помощью Driver Verifier (с которым вы познакомились в главе 7) перехватывать операции некорректно написанных драйверов, приводящие к повреждению системы, и получать аварийные дампы, анализ которых может выявить проблему.

Notmyfault

Различные виды краха системы, рассматриваемые здесь, можно вызвать с помощью утилиты Notmyfault (wwwsysintemals.com/windowsinternals). Notmyfault состоит из исполняемого файла Notmyfault.exe и драйвера Myfault.sys. Когда вы запускаете исполняемый файл Notmyfault, он загружает драйвер и выводит диалоговое окно, показанное на рис. 14-7. B этом окне вы можете выбрать различные варианты краха системы или указать, что драйвер должен вызвать утечку памяти из пула подкачиваемой памяти. Доступны наиболее распространенные (по статистике Microsoft Product Support Services) виды краха системы. После того как вы выбрали параметр и щелкнули кнопку Do Bug, исполняемый файл через API-функцию DeviceIoControl обращается к драйверу и указывает ему, ошибка какого типа должна произойти. Заметьте: лучше экспериментировать, вызывая крах системы через Notmyfault, на тестовой или виртуальной системе, так как полностью исключить вероятность того, что поврежденная память не будет записана на диск, нельзя.

ПРИМЕЧАНИЕ Имена исполняемого файла и драйвера Notmyfault («не моя вина») отражают тот факт, что приложение, выполняемое в пользовательском режиме, не может напрямую вызвать крах системы. Исполняемый файл Notmyfault способен вызвать крах системы, только загрузив драйвер, который выполнит запрещенную операцию в режиме ядра.

Базовый анализ

Самый простой для отладки крах вызывается при выборе переключателя High IRQL Fault (Kernelmode) и нажатии кнопки Do Bug. Тогда драйвер выделит страницу в пуле подкачиваемой памяти, освободит страницу, поднимет уровень IRQL выше «DPC/dispatch», а затем обратится к освобожденной странице. (Об IRQL см. главу 3.) Если это не приведет к краху, система продолжит считывать память после конца страницы до тех пор, пока не произойдет крах из-за обращения к недействительной странице. Таким образом, драйвер выполняет несколько недопустимых операций.

1. Ссылается на память, которая ему не принадлежит.

2. Обращается к пулу подкачиваемой памяти при IRQL уровня «DPC/dispatch» или выше, что недопустимо, так как при таких IRQL ошибки страниц не разрешены.

3. Выходит за конец выделенной области памяти и пытается обратиться к памяти, которая потенциально может быть недействительной. Первое обращение к странице не обязательно должно вызвать крах, если страница, освобожденная драйвером, остается в системном рабочем наборе. (O системном рабочем наборе см. главу 7.)

Загрузив в Kd аварийный дамп, сгенерированный при таком крахе, вы увидите следующие результаты:

Прежде всего следует заметить, что Kd сообщает об ошибках при загрузке символов для Myfault.sys и Notmyfault.exe. Этого можно было ожидать, поскольку файлы символов для них нельзя обнаружить по пути поиска файлов символов (который указывает на сервер символов Microsoft). Вы будете получать аналогичные ошибки для драйверов сторонних производителей и исполняемых файлов, не входящих в операционную систему.

Текст, содержащий результаты анализа, достаточно краток: показаны числовой стоп-код и контрольные параметры, далее идет строка «probably caused by». B ней указан драйвер, который, с точки зрения механизма анализа, является наиболее вероятной причиной ошибки. B данном случае наш драйвер попал на заметку, и эта строка указывает прямо на Myfault.sys, поэтому проводить анализ вручную нет нужды.

Строка «Followup», как правило, не несет полезной информации — эти данные используются в Microsoft, когда отладчик ищет имя модуля в файле Triage.ini, содержащемся в подкаталоге Triage установочного каталога Debugging Tools for Windows. B версии этого файла, используемой внутри Microsoft, перечислены разработчики или группы, которые должны анализировать крах системы, вызываемый тем или иным драйвером, и, если удалось найти разработчика или группу, соответствующее имя выводится в строке Followup.

Детальный анализ

Bo всех случаях, даже когда удалось выявить сбойный драйвер с помощью базового анализа аварийного дампа Notmyfault, нужно проводить детальный анализ командой:

!analyze — v

Первое очевидное отличие детального анализа и анализа по умолчанию состоит в том, что в первом случае выводится описание стоп-кода и его параметров. Ниже приведен вывод этой команды для того же дампа:

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

Другая потенциально полезная информация, выводимая при детальном анализе — трассировочные данные стека потока, выполнявшегося в момент краха. Вот как она выглядит для того же дампа:

Приведенный выше стек показывает, что образ исполняемого файла Not-myfaul, показанный внизу, вызывал функцию DeviceIoControl в Kernel32.dll, которая в свою очередь вызвала ZwDeviceIoControlFile в Ntdl.dll, и т. д., пока система, наконец, не рухнула при выполнении инструкции в образе Myfault. Стеки вызовов, подобные этому, могут оказаться полезными, поскольку иногда причиной краха системы является то, что один драйвер передал другому неправильно отформатированные, поврежденные или недопустимые параметры. Драйвер, передавший некорректные данные, способные вызвать крах системы, можно выявить при анализе, просмотрев стек вызовов, из которого видно, что было обращение к другому драйверу B данном простом примере в стеке вызовов показан только драйвер myfault. (Модуль «nt» — это Ntoskrnl.)

Если вам не известен драйвер, выявленный при анализе, выполните команду Im (аббревиатура от «list modules»), чтобы посмотреть информацию о версии драйвера. Укажите параметры k (kernel modules), v (verbose), m (match), а затем имя драйвера и символ подстановки:

Вы можете идентифицировать назначение драйвера по описанию, а также выяснить по версии файла и продукта, установлена ли у вас самая последняя версия. (Это можно определить, например, посетив сайт разработчика драйвера.) Если информация о версии отсутствует, например в момент краха соответствующая страница была выгружена из физической памяти, вы получите ее из свойств файла образа драйвера: просмотрите их с помощью Windows Explorer.

Средства анализа проблем, вызывающих крах

B предыдущем разделе, когда мы вызвали крах системы, выбрав параметр High IRQL Fault (Kernelmode) в Notmyfault, автоматический анализ дампа в отладчике не составил труда. Увы, в большинстве случаев исследовать крах системы с помощью отладчика сложно, а зачастую и невозможно. Существует несколько уровней верификации (с нарастающей степенью сложности и пропорциональным падением производительности системы), которые позволяют добиться того, чтобы вместо дампа, непригодного для анализа, генерировался дамп, пригодный для анализа. Если после настройки системы в соответствии с требованиями одного уровня и перезагрузки, вам не удалось выявить причину краха, попробуйте перейти на следующий уровень.

1. Если вы считаете, что крах системы может вызывать один или несколько драйверов, поскольку они были установлены в систему относительно недавно или их недавно обновили, или это следует из обстоятельств, при которых система терпит крах, то включите верификацию этих драйверов в Driver Verifier и выберите все режимы верификации, кроме имитации нехватки ресурсов.

2. Задайте тот же уровень верификации, но для всех неподписанных драйверов в системе. Или, если вы работаете с Windows 2000, в которой Driver Verifier не делает различий между подписанными и неподписанными драйверами, включите верификацию всех драйверов, поставляемых не Microsoft, а другими компаниями.

3. Задайте тот же уровень верификации, но для всех драйверов системы. Чтобы сохранить приемлемую производительность, можно разбить драйверы на группы и в промежутках между перезагрузками активизировать Driver Verifier для какой-то одной группы драйверов.

Очевидно, прежде чем тратить время и силы на изменение конфигурации системы и анализ аварийных дампов, стоит убедиться в том, что используются последние версии компонентов ядра и драйверов сторонних поставщиков, и при необходимости обновить их через Windows Update или напрямую через сайты производителей устройств.

ПРИМЕЧАНИЕ Если загрузка вашей системы стала невозможной из-за того, что Driver Verifier обнаруживает ошибку драйвера и вызывает крах системы, загрузите систему в безопасном режиме (в котором верификация отключена), запустите Driver Verifier и отключите параметры проверки.

B следующих разделах показывается, как с помощью Driver Verifier сделать так, чтобы вместо дампов, непригодных для отладки, создавались дампы, позволяющие решить проблему. Кроме того, почитайте справочный файл Debugging Tools, где есть руководства по методикам углубленной отладки.

Переполнение буфера и особый пул

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

ПРИМЕЧАНИЕ Чтобы облегчить выявление этих трудноуловимых повреждений, в Windows XP Service Pack 2 (или выше) всегда выполняется проверка выхода за границы блока в пуле (pool-block tail checking). Поэтому переполнение буфера скорее всего тут же приведет к краху BAD_POOL_HEADER.

Вы можете вызвать крах, связанный с переполнением буфера, запустив Notmyfault и выбрав переключатель Buffer Overflow. B этом случае Myfault выделит память под буфер и перезапишет 40 байтов, идущих после буфера. Между щелчком кнопки Do Bug и крахом системы может пройти довольно много времени, возможно, вам даже придется задействовать пул, запустив какие-либо приложения. Это еще раз подчеркивает, что повреждение может не скоро привести к последствиям, влияющим на стабильность системы. Анализ аварийного дампа, полученного при такой ошибке, почти всегда показывает, что проблема связана с Ntoskrnl или каким-либо другим драйвером. И это демонстрирует бесполезность детального анализа при таком описании стоп-кода:

B описании стоп-кода рекомендуется запустить Driver Verifier для каждого нового или подозрительного драйвера или активизировать особый пул с помощью Gflags. B обоих случаях преследуется одна и та же цель: выявить потенциальное повреждение в момент, когда оно происходит, и вызвать крах системы так, чтобы при автоматическом анализе удалось обнаружить драйвер, вызвавший повреждение.

Если в Driver Verifier включен режим особого пула, проверяемые драйверы используют специальный пул вместо пула подкачиваемой или неподкачиваемой памяти во всех случаях, когда выделяется память для буферов размера, немного меньшего размера страницы. Буфер, память под который выделяется из особого пула, заключен между двумя недействительными страницами и по умолчанию выравнивается по верхней границе страницы. Кроме того, подпрограммы управления особым пулом заполняют неиспользуемое пространство страницы, содержащей буфер, по случайному шаблону. Ha рис. 14-8 показано, как выделяется память из особого пула.

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

Чтобы посмотреть, как с помощью особого пула вызвать крах системы, который легко диагностировать с помощью механизма автоматического анализа, запустите DriverVerifier Manager (Диспетчер проверки драйверов). В Windows 2000 перейдите на вкладку Settings (Параметры), введите myfault.sys в текстовое поле внизу страницы, предназначенное для задания дополнительных драйверов, установите флажок особого пула, сохраните изменения, выйдите из Driver Verifier Manager и перезагрузитесь. B Windows XP и Windows Server 2003 выберите Create Custom Settings (For Code Developers) [Создать не стандартные параметры (для кода программ)] на первой странице мастера, на второй — Select Individual Settings From A Full List (Выбрать параметры из списка), на третьей — Special Pool (Особый пул). Далее выберите Select Drivers From A List (Выберите имя драйвера из списка), а на странице, где перечислены типы драйверов, введите myfault.sys в диалоговом окне, открываемом после нажатия кнопки добавления незагруженных драйверов. (He ищите в этом диалоговом окне файл myfault.sys — просто введите его имя.) Затем отметьте драйвер myfault.sys, выйдите из мастера и перезагрузитесь.

Когда вы запустите Notmyfault и вызовете переполнение буфера, сразу же произойдет крах системы, а анализ дампа даст следующий результат:

Probably caused by: myfault.sys (myfault+3f1)

При детальном анализе вы получите следующее описание стоп-кода:

Благодаря особому пулу трудноуловимая ошибка немедленно проявила себя, и анализ стал тривиальным.

Перезапись кода и защита системного кода от записи

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

Если вы запустите Notmyfault и выберете переключатель Code Overwrite, драйвер Myfault повредит точку входа функции NtReadFile. Далее возможны два варианта. Если ваша система работает под управлением Windows 2000 и оснащена не более чем 127 Мб физической памяти или работает под управлением Windows XP или Windows Server 2003 и оснащена не более чем 255 Мб физической памяти, произойдет крах и анализ дампа укажет на Myfault.sys.

B описании стоп-кода, выводимом при детальном анализе, говорится, что драйвер Myfault попытался записать данные в память, доступную только для чтения:

ATTEMPTED_WRITE_TO_READONLY_MEMORY (be)

An attempt was made to write to readonly memory. The guilty driver is on the stack trace (and is typically the current instruction pointer). When possible, the guilty driver's name (Unicode string) is printed on the bugcheck screen and saved in KiBugCheckDriver.

Однако, если у вас Windows 2000 и более 127 Мб памяти либо Windows XP или Windows Server 2003 и более 255 Мб памяти, произойдет крах другого типа, так как повреждение памяти сразу не проявится. Поскольку NtReadFile — широко используемая системная функция, к которой подсистема Windows обращается при считывании ввода с клавиатуры или от мыши, крах системы произойдет почти сразу же, как только какой-либо поток попытается выполнить поврежденный код. Возникнет ошибка из-за выполнения недопустимой инструкции. Анализ аварийного дампа, выполняемый в этом случае, может давать разные результаты, но они обязательно будут неправильными. Обычно механизм анализа приходит к выводу, что наиболее вероятными источниками ошибки являются Windows.sys или Ntoskrnl.exe. При таком крахе выводится следующее описание стоп-кода:

Разные конфигурации ведут себя по-разному в связи с тем, что в Windows 2000 введен механизм защиты системного кода от записи (system code write protection). B таблице 14-2 показано, в каких конфигурациях защита системного кода от записи не используется по умолчанию.

Если защита системного кода от записи включена, диспетчер памяти проецирует Ntoskrnl.exe, HAL и загрузочные драйверы как стандартные физические страницы (4 Кб для x86 и x64, 8 Кб для IA64). Поскольку при проецировании образов обеспечивается детализация с точностью до размера стандартной страницы, диспетчер памяти может защитить страницы, содержащие код, от записи и генерировать ошибку доступа при попытке их модификации (что вы и видели при первом крахе). Ho когда защита системного кода от записи отключена, диспетчер памяти использует при проецировании Ntoskrnl.exe большие страницы (4 Мб для x86 или 16 Мб для IA64 и x64). Taкой режим по умолчанию действует в Windows 2000 при наличии более чем 127 Мб памяти, а в Windows XP или Windows Server 2003 — при наличии более чем 255 Мб памяти. Диспетчер памяти не может защитить код, поскольку код и данные могут находиться на одной странице.

Если защита системного кода от записи отключена и при анализе аварийного дампа сообщается о маловероятных причинах краха или если вы подозреваете, что произошло повреждение кода, следует включить защиту. Для этого проще всего включить проверку хотя бы одного драйвера с помощью Driver Verifier. Кроме того, можно включить защиту вручную, добавив два параметра в раздел реестра HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management. Сначала укажите максимально возможное значение для объема памяти, начиная с которого диспетчер памяти использует при проецировании Ntoskrnl.exe большие страницы вместо стандартных. Создайте параметр LargePageMinimum типа DWORD, присвойте ему значение 0xFFFFFFFF. Добавьте еще один параметр типа DWORD — Enforce-WriteProtection — и присвойте ему значение 1. Чтобы изменения вступили в силу, перезагрузите компьютер.

ПРИМЕЧАНИЕ Когда отладчик имеет доступ к файлам образов, включенным в аварийный дамп, при анализе на внутреннем уровне выполняется команда отладчика!chkimg, которая проверяет, совпадает ли копия образа в аварийном дампе с образом на диске, и сообщает о различиях. Заметьте: если вы активизируете Driver Verifier, chkimg обязательно обнаружит различия при сравнении с файлом Ntoskrnl.exe.

Углубленный анализ аварийных дампов

B предыдущем разделе рассказывалось о том, как с помощью Driver Verifier получать аварийные дампы, автоматический анализ которых может решить проблему. Тем не менее, возможны случаи, когда невозможно добиться, чтобы система сгенерировала дамп, который легко проанализировать. B таких случаях нужен анализ вручную, чтобы попытаться определить, в чем заключается проблема.

• C помощью команды отладчика !process 0 0 посмотрите, какие процессы выполняются, и убедитесь, что вам понятно назначение каждого из них. Попробуйте завершить или удалить приложения и сервисы, без которых можно обойтись.

• C помощью команды Im с параметром kv выведите список загруженных драйверов режима ядра. Убедитесь, что вам понятно назначение каждого из драйверов сторонних поставщиков и что вы используете самые последние версии.

• C помощью команды !vm проверьте, не исчерпаны ли виртуальная память системы, пул подкачиваемой памяти и пул неподкачиваемой памяти. Если исчерпана виртуальная память, объем переданных страниц будет близок к пределу. B этом случае попытайтесь выявить потенциальную утечку памяти: просмотрите список процессов и выберите те из них, которым передано много памяти. Если исчерпан пул подкачиваемой или неподкачиваемой памяти (т. е. объем занятой памяти близок к максимуму), см. эксперимент «Анализ утечки памяти в пуле» в главе 7.

Существуют и другие отладочные команды, которые могут оказаться полезными, но для их применения нужны более глубокие знания. Одной из таких команд является /irp. B следующем разделе показано, как с ее помощью идентифицировать подозрительные драйверы.

Засорение стека

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

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

B различных случаях краха анализ аварийного дампа, проводимый при переполнении стека, будет указывать на разные драйверы, но стоп-код всегда будет одним и тем же — KMODE_EXCEPTION_NOT_HANDLED. Если вы выполните детальный (verbose) анализ, трассировочная информация для стека будет выглядеть так:

STACK_TEXT:

b7bOebd4 00000000 00000000 00000000 00000000 0x0

Это объясняется тем, что мы перезаписываем стек нулями. K сожалению, такие механизмы, как особый пул и защита системного кода от записи, не позволяют выявлять «баги» этого типа. Придется выполнять анализ вручную, по косвенным признакам определяя, какой драйвер выполнялся в момент повреждения стека. Один из возможных вариантов — исследовать IRP-паке-ты, с которыми работает поток, выполняемый в момент засорения стека. Когда поток передает запрос ввода-вывода, диспетчер ввода-вывода записывает указатель на соответствующий IRP в список Irp, хранящийся в структуре ETHREAD потока. Команда отладчика /thread выводит дамп этого списка для заданного потока. (Если адрес объекта «поток» не указан, команда !thread выводит дамп для текущего потока, выполняемого процессором.) Затем IRP можно изучить с помощью команды !irp\

Вывод показывает, что текущий и единственный фрагмент стека для IRP (обозначенный префиксом «›») принадлежит драйверу Myfault. Если бы это было на практике, далее следовало бы убедиться, что установлена последняя версия драйвера, и, если это не так, установить новую версию. Если это не помогло, нужно было бы активизировать Driver Verifier для данного драйвера (включив все режимы, кроме имитации нехватки памяти).

Зависание или отсутствие отклика системы

Если система перестает отвечать (т. е. не реагирует на ввод с клавиатуры или мыши, курсор мыши не перемещается или вы можете перемещать курсор, но система не реагирует на щелчки), говорят, что система зависла. Существует несколько возможных причин зависания системы:

• при обращении к драйверу устройства ISR (interrupt service routine) или DPC не вернула управление;

• поток с высоким приоритетом (выполняемый в режиме реального времени) вытеснил потоки ввода данных в подсистему управления окнами (windowing system);

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

Если вы работаете с Windows XP или Windows Server 2003, то можете выявлять взаимные блокировки, используя одну из функций Driver Verifier — обнаружение взаимных блокировок (deadlock detection). При обнаружении взаимных блокировок ведется наблюдение за спин-блокировками (spin locks), быстрыми и обычными мьютексами и выявляются закономерности, которые могут приводить к взаимной блокировке. (Информацию об этих и других синхронизирующих примитивах см. в главе 3.) Если обнаружена такая ситуация, Driver Verifier вызывает крах системы, указывая, какой драйвер является причиной взаимной блокировки. Простейшая форма взаимной блокировки — каждый из двух потоков удерживает некий ресурс, нужный другому потоку, при этом ни один из них не освобождает свой ресурс и ждет освобождения другого ресурса. Если вы используете Windows XP или Windows Server 2003, первое, что нужно сделать для устранения зависаний системы, — включить обнаружение взаимных блокировок для подозрительных драйверов, затем для неподписанных драйверов, а затем для всех драйверов. B этом режиме следует работать до тех пор, пока не произойдет крах системы, который позволит выявить драйвер, вызывающий взаимную блокировку.

Если вы используете Windows 2000 или если вы проверили все драйверы, а система продолжает зависать, то должны либо вручную вызвать крах зависшей системы и проанализировать полученный в результате дамп, либо исследовать систему с помощью отладчика ядра.

Итак, есть два подхода к исследованию зависающей системы, позволяющие выявить драйвер или компонент, который вызывает зависания. Первый — вызвать крах зависшей системы и надеяться, что будет получен дамп, который удастся проанализировать. Второй — исследовать систему с помощью отладчика ядра и проанализировать работу системы. И при том, и при другом подходе необходимы предварительная настройка и перезагрузка. Чтобы выявить и устранить причину зависания, в обоих случаях выполняется одно и то же исследование состояния системы.

Чтобы вручную вызвать крах зависшей системы, сначала добавьте в реестр параметр HKLM\System\CurrentControlSet\Services\i8042prt\Parameters\ CrashOnCtrlScroll типа DWORD со значением 1. После перезагрузки порт-драйвер i8042, который является драйвером порта ввода с PS/2-клавиатуры, будет наблюдать за нажатиями клавиш в своей ISR (об ISR подробно рассказывается в главе 3) и отслеживать двукратное нажатие клавиши Scroll Lock при нажатой правой клавише Ctrl. Обнаружив такую последовательность нажатий, драйвер вызывает функцию KeBugCheckEx со стоп-кодом MANUALLY_INITIATED_CRASH (0xE2), указывающим, что крах инициирован пользователем вручную. Когда система перезагрузится, откройте аварийный дамп и с помощью методик, описанных выше, попробуйте установить, почему система зависла (например, определите, какой поток выполнялся, когда система зависла, попытайтесь понять, что произошло, проанализировав стек ядра и т. д.). Заметьте: этот подход работает в большинстве случаев зависания систем, но не годится, когда ISR порт-драйвера i8042 не выполняется. (Эта ISR не выполняется, если все процессоры зависли из-за того, что их IRQL выше, чем IRQL у ISR, или если повреждение системных структур данных затронуло код либо данные, используемые при обработке прерываний.)

ПРИМЕЧАНИЕ Вызов краха зависшей системы вручную на основе функциональности порт-драйвера i8042 невозможен при использовании USB-клавиатур. Этот подход работает только в случае PS/2-клавиатур.

Еще один способ вручную вызвать крах системы — использовать встроенную кнопку «crash». (Она имеется на некоторых серверах класса «high end».) Тогда, чтобы инициировать крах, материнская плата системы генерирует NMI (немаскируемое прерывание). Чтобы активизировать эту функцию, задайте значение 1 для содержащегося в реестре DWORD-параметра HKLM\ System\CurrentControlSet\Control\CrashControl\NMICrashDump. B этом случае при нажатии кнопки «crash» в системе будет генерироваться NMI, и обработчик NMI-прерываний ядра вызовет KeBugCbeckEx. Такой подход более универсален, чем применение порт-драйвера i8042, поскольку IRQL у NMI всегда выше, чем у прерывания порт-драйвера i8042. Дополнительные сведения см. по ссылке http://www.microsoft.com/platform/proc/dmpsw.asp.

Если сгенерировать аварийный дамп вручную нельзя, попытайтесь исследовать зависшую систему. Прежде всего загрузите систему в отладочном режиме. Это можно сделать двумя способами. Нажмите клавишу F8 во время загрузки и выберите Debugging Mode (Режим отладки) или добавьте запись, задающую загрузку в отладочном режиме, в файл Boot.ini: скопируйте запись, которая уже имеется в файле Boot.ini системы, и добавьте ключ /DEBUG. При нажатии F8 система будет использовать соединение по умолчанию (последовательный порт COM2 и скорость 19200 бод). При использовании режима /DEBUG вы должны будете настроить механизм соединения между хост-системой, на которой выполняется отладчик ядра, и целевой системой, загружаемой в отладочном режиме, и задать ключи /Debugport и /Baudrate, соответствующие типу соединения. Доступно два типа соединения: нуль-модемный кабель, соединяющий последовательные порты, или (в системах Windows XP и Windows Server 2003) кабель IEEE 1394 (Firewire), подключенный к порту 1394 каждой системы. Подробности настройки хост-системы и целевой системы для отладки ядра см. в справочном файле Windows Debugging Tools.

При загрузке в отладочном режиме система загружает отладчик ядра и готовит его к соединению с отладчиком ядра, выполняемом на другом компьютере, подключенном по нуль-модемному кабелю или по IEEE 1394. Заметьте: присутствие отладчика ядра не влияет на производительность. Когда система зависнет, запустите отладчик Windbg или Kd на подключенной системе, установите соединение между отладчиками ядра и выполните отладку кода зависшей системы. Такой подход не сработает, если прерывания отключены или если поврежден код отладчика ядра.

ПРИМЕЧАНИЕ Загрузка системы в отладочном режиме не влияет на производительность, если эта система не соединена с другой. Однако этого нельзя сказать о системе, настроенной на автоматическую перезагрузку после краха: если при загрузке системы включена отладка ядра, то после краха системы отладчик ядра будет ожидать соединения с другой системой.

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

Зависание можно вызвать, запустив Notmyfault и выбрав параметр Hang. Тогда драйвер Myfault поставит в очередь DPC, выполняющую бесконечный цикл для каждого процессора системы. Поскольку при выполнении DPC-функ-ций IRQL процессора имеет уровень «DPC/dispatch», ISR клавиатуры будет реагировать на последовательность нажатий клавиш, вызывающую крах.

Когда вы приступили к отладке зависшей системы или загрузили в отладчик дамп, который вручную сгенерировали для зависшей системы, следует выполнить команду !analyze с параметром — hang. Тогда отладчик проанализирует блокировки системы и попытается определить, не произошла ли взаимная блокировка, и, если да, то какой драйвер или драйверы в ней участвуют. Однако, если зависание аналогично вызванному программой Notmyfault, команда !analyze не сообщит ничего полезного.

Если команда !analyze не помогла решить проблему, выполните команды !thread и !process в каждом из контекстов процессоров для дампа. (Для переключения между контекстами процессоров используйте команду ~, например ~1 переключает в контекст процессора 1.) Если поток, вызвавший зависание системы, выполняет бесконечный цикл на уровне IRQL «DPC/dispatch» или выше, вы увидите модуль драйвера, в котором это происходит, в трассировочной информации стека, выводимой командой !thread. Если зависание системы вызвано программой Notmyfault, трассировочная информация стека, получаемая по аварийному дампу системы, выглядит так:

Первые несколько строк трассировочной информации стека относятся к подпрограммам, вызванным, когда вы нажали клавиши, по которым порт-драйвер i8042 вызывает крах системы. Присутствие драйвера Myfault означает, что зависание системы могло произойти из-за него.

Еще одна команда, которая может оказаться полезной, — !locks; она выводит состояние всех блокировок ресурсов исполнительной системы. По умолчанию команда показывает только спорные ресурсы, т. е. ресурсы, на владение которыми претендуют минимум два потока. Исследуйте стеки потоков, владеющих такими ресурсами, с помощью команды !thread, и посмотрите, какому драйверу они могут принадлежать.

Если аварийного дампа нет

B этом разделе мы рассмотрим, как устранять неполадки в системах, которые по каким-либо причинам не записывают аварийный дамп. Аварийный дамп может не записываться из-за того, что размер страничного файла на загрузочном томе слишком мал, чтобы вместить дамп, или из-за того, что на диске недостаточно места, чтобы извлечь дамп после перезагрузки. Эти две причины легко устранить, увеличив размер страничного файла или задав при настройке, что дамп сохраняется на томе, где достаточно места.

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

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

Одно из простых решений — отключить параметр Automatically Restart (Выполнить автоматическую перезагрузку) в параметрах Startup And Recovery (Загрузка и восстановление), чтобы можно было изучать «синий экран» с консоли. Однако текст «синего экрана» позволяет выявить причины краха системы только в самых простых случаях.

Для более глубокого анализа необходимо с помощью отладчика ядра исследовать поведение системы в момент краха. Для этого загрузите систему в отладочном режиме, о котором рассказывалось в предыдущем разделе. Когда происходит крах системы, загруженной в отладочном режиме, она не выводит «синий экран» и не пытается записать дамп, а ожидает соединения с отладчиком ядра, выполняемым на хост-системе. Поэтому можно увидеть, что вызвало причину краха, и, вполне вероятно, провести некий базовый анализ с помощью команд отладчика ядра, описанных ранее. Как говорилось в предыдущем разделе, команда отладчика позволяет сохранить копию памяти системы, потерпевшей крах, для дальнейшей отладки, что даст возможность перезагрузить эту систему и вести отладку в автономном режиме.

ЭКСПЕРИМЕНТ: экранная заставка Blue Screen

Отличный способ вспомнить, как выглядит «синий экран», или подшутить над своими друзьями и коллегами — запустить экранную заставку Sysinternals Blue Screen, которую можно скачать с сайта wwwsysinter nah.com. Она точно имитирует «синий экран» для той версии Windows, в которой вы работаете, и выводит системную информацию (например, список загруженных драйверов), соответствующую действительности. Кроме того, она имитирует автоматическую перезагрузку, показывая экран запуска Windows. Заметьте: в отличие от других экранных заставок, исчезающих при перемещении мыши, Blue Screen требует нажатия клавиши.

C помощью утилиты Psexec с сайта Sysinternals вы даже можете запустить экранную заставку на другой системе, выполнив команду:

psexec \\computername — i — d "c: \sysinternals bluescreen.scr" — s

Для этого у вас должны быть административные привилегии на удаленной системе. (C помощью ключей — и и — p утилиты Psexec можно задать другие удостоверения защиты.) Проверьте, есть ли у ваших коллег чувство юмора!