Лекция 24. Кэширование в Windows. Ввод-вывод в Windows. Файловая система Windows nt



страница1/3
Дата16.02.2017
Размер0.97 Mb.
Просмотров194
Скачиваний0
ТипЛекция
  1   2   3

Лекция 24. Кэширование в Windows. Ввод-вывод в Windows. Файловая система Windows NT

1. Кэширование в Windows

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

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

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

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

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



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

2. Ввод-вывод в Windows.

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

2.1. Фундаментальные концепции

Диспетчер ввода-вывода находится в чрезвычайно близких отношениях с диспетчером Plug-and-Play. Основная идея Plug-and-Play состоит в том, что существует перечислимая шина. Многие шины (в том числе PC Card, PCI, PCIe, AGP, USB, IEEE-1394, E-IDE и SATA) были спроектированы таким образом, чтобы диспетчер Plug-andPlay мог послать запрос на каждый разъем шины и попросить установленное в нем устройство идентифицироваться. Обнаружив, что именно там установлено, диспетчер Plug-and-Play назначает аппаратные ресурсы (такие, как уровни прерываний), находит соответствующие драйверы и загружает их в память. При загрузке каждого драйвера для него создается объект драйвера (driver object). Затем для каждого устройства назначается как минимум один объект устройства. Для некоторых шин (таких, как SCSI) перечисление происходит только в момент загрузки, но для других шин (таких, как USB) оно может произойти в любой момент, что требует тесного взаимодействия между диспетчером Plug-and-Play, драйверами шины (которые фактически и выполняют перечисление) и диспетчером ввода-вывода.

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

Интересная функциональная возможность Windows — поддержка динамических дисков (dynamic disks). Эти диски могут простираться на несколько разделов и даже несколько дисков и могут переконфигурироваться на ходу, без необходимости в перезагрузке. То есть логические диски больше не ограничены одним разделом (или даже одним диском), так что одна файловая система может захватывать несколько дисков.

Ввод-вывод для томов данных может фильтроваться специальным драйвером Windows, который реализует теневые копии томов (Volume Shadow Copies). Драйвер-фильтр создает моментальный снимок тома, который можно монтировать отдельно и который представляет собой том данных в некий предыдущий момент времени. Он делает это, отслеживая изменения после выполнения моментального снимка. Это очень удобно для восстановления файлов, которые были случайно удалены, или возвращения обратно в предыдущие моменты времени (чтобы увидеть состояние файла во время выполнения ранее моментальных снимков).

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

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

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

Последний аспект, который мы упомянем, — это приоритетный ввод-вывод. Приоритет ввода-вывода определяется приоритетом потока (либо его можно указать явным образом). Есть пять уровней приоритета: critical (критический), high (высокий), normal (нормальный), low (низкий) и very low (очень низкий). Критический зарезервирован для диспетчера памяти (во избежание взаимоблокировок, которые в противном случае могли бы произойти в периоды острой нехватки памяти). Низкий и очень низкий приоритеты используются фоновыми процессами (службой дефрагментации дисков или ведущими поиск шпионского программного обеспечения сканерами, а также поиском по компьютеру — все эти процессы стараются не создавать помех для нормальной работы системы). Большинство операций ввода-вывода получают нормальный приоритет, но мультимедийные приложения могут пометить свой ввод-вывод высоким приоритетом (чтобы избежать проблем). Мультимедийные приложения могут также использовать резервирование полосы пропускания (bandwidth reservation) — они запрашивают гарантированную ширину полосы пропускания для обращения к критичным в смысле временных задержек файлам (вроде музыки или видео). Система ввода-вывода сообщит приложению оптимальный размер передачи и количество ожидающих выполнения операций ввода-вывода (которое следует поддерживать для того, чтобы система ввода-вывода гарантированно достигла запрошенной ширины полосы пропускания).

2.2. Вызовы интерфейса прикладного программирования ввода-вывода

Интерфейсы системных вызовов, предоставляемые диспетчером ввода-вывода, не очень отличаются от предлагаемых большинством других операционных систем. Основные операции: open, read, write, ioctl и close, но есть и другие операции: Plug-and-Play, управления энергопотреблением, установки параметров, сброса системных буферов и т. д На уровне Win32 эти API заключаются в оболочку интерфейсов, которые предоставляют операции более высокого уровня (специфичные для конкретных устройств). На нижнем уровне эти оболочки открывают устройства и выполняют основные операции. Даже некоторые операции метаданных (такие, как переименование файла) реализованы без специфичных системных вызовов. Они просто используют специальную версию операции ioctl. Это станет более понятно, когда мы объясним реализацию стеков устройств ввода-вывода и использование пакетов запросов ввода-вывода (I/O request packets (IRP)) диспетчером ввода-вывода.

Собственные системные вызовы ввода-вывода NT (в соответствии с общей философией Windows) имеют множество параметров и много вариантов. В табл. 11.15 перечислены основные интерфейсы системных вызовов диспетчера ввода-вывода.

Таблица 11.15. Собственные вызовы интерфейса прикладного программирования

NT для выполнения ввода-вывода



Системный вызов вводавывода

Описание

NtCreateFile

Открыть новый или существующий файл либо устройство

NtReadFile

Читать из файла или устройства

NtWriteFile

Писать в файл или устройство

NtQueryDirectoryFile

Запросить информацию о каталоге (включая файлы)

NtQueryVolumeInformationFile

Запросить информацию о томе

NtSetVolumeInformationFile

Модифицировать информацию тома

Системный вызов вводавывода

Описание

NtNotifyChangeDirectoryFile

Завершается, когда любой файл каталога или его подкаталогов будет модифицирован

NtQueryInformationFile

Запросить информацию о файле

NtSetInformationFile

Модифицировать информацию файла

NtLockFile

Заблокировать диапазон байтов файла

NtUnlockFile

Снять блокировку диапазона

NtFsControlFile

Различные операции с файлом

NtFlushBuffersFile

Сбросить находящиеся в памяти файловые буферы на диск

NtCancelIoFile

Отменить невыполненные операции ввода-вывода для файла

NtDeviceIoControlFile

Специальные операции с устройством

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

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

NtNotifyChangeDirectoryFile — это пример интересной парадигмы NT. Потоки могут выполнять ввод-вывод, чтобы определить, происходят ли с объектами какие-либо изменения (в основном это каталоги файловых систем, как в данном случае, или ключи реестра). Поскольку ввод-вывод асинхронный, то поток возвращается и продолжает выполнение (а позже уведомляется, когда что-то модифицируется). Незавершенный запрос ставится в очередь в файловой системе как ожидающая выполнения операция ввода-вывода (с использованием пакета запросов IRP). Если вы хотите удалить том с файловой системой из компьютера, то извещения становятся проблемой, поскольку есть незавершенные операции ввода-вывода. Поэтому Windows поддерживает средства для отмены незавершенных операций ввода-вывода (в том числе в файловых системах есть поддержка принудительного размонтирования тома с незавершенными операциями ввода-вывода).

NtQueryInformationFile — это специфичная (для файлов) версия системного вызова для каталогов. У нее есть напарник — системный вызов NtSetInformationFile. Эти интерфейсы обращаются и модифицируют все виды информации об именах файлов, файловых функциональных возможностях (наподобие шифрования, сжатия и разреженности), а также прочих атрибутах файлов (и их подробностях). И в том числе они определяют внутренний идентификатор файла или присваивают файлу уникальное двоичное имя (идентификатор объекта).

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

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

Аналогичные средства имеются и в UNIX, но там приложения соблюдают блокировки диапазонов по своему усмотрению. NtFsControlFile во многом похожа на предыдущие операции query и set, но является операцией более общего типа, нацеленной на обработку специфичных для файлов операций (которые не выполняются другими вызовами). Например, некоторые операции специфичны для конкретной файловой системы.

И наконец, существуют разнообразные вызовы типа NtFlushBuffersFile. Подобно вызову sync операционной системы UNIX, он заставляет записать данные файловой системы на диск. NtCancelIoFile отменяет незавершенные запросы ввода-вывода для конкретного файла. NtDeviceIoControlFile реализует операции ioctl для устройств. Список операций на самом деле гораздо длиннее. Существуют системные вызовы для удаления файлов по имени, для запроса атрибутов конкретного файла, но это просто оболочки для других операций диспетчера ввода-вывода (которые мы уже перечислили), и их не нужно реализовывать в виде отдельных системных вызовов. Есть также системные вызовы для работы с портами завершения ввода-вывода (I/O completion ports) — это средство формирования очередей в Windows, которое помогает многопоточным серверам эффективно использовать операции асинхронного ввода-вывода (за счет подготовки потоков по требованию и уменьшения количества переключений контекста, требуемых для обслуживания ввода-вывода специальными потоками).

2.3. Реализация ввода-вывода

Система ввода-вывода в Windows состоит из служб Plug-and-Play, диспетчера электропитания, менеджера ввода-вывода, а также модели драйвера устройств. Plug-and-Play обнаруживает изменения в конфигурации аппаратного обеспечения, создает (или уничтожает) стеки устройств (для каждого устройства), а также загружает и выгружает драйверы устройств. Диспетчер электропитания настраивает состояние электропитания устройств ввода-вывода, чтобы уменьшить потребление энергии системой, когда устройства не используются. Диспетчер ввода-вывода предоставляет поддержку манипулирования объектами ядра для ввода-вывода, а также операций типа IoCallDrivers и IoCompleteRequest. Однако большая часть работы по поддержке ввода-вывода в Windows реализована в самих драйверах устройств.

Драйверы устройств

Чтобы гарантировать, что драйверы устройств хорошо работают с Windows, компания Microsoft описала модель WDM (Windows Driver Model), которой должны соответствовать драйверы устройств. Существует набор разработчика (Windows Driver Kit), который содержит документацию и примеры, помогающие создавать драйверы, соответствующие WDM. Большинство драйверов Windows начинается с копирования подходящего образцового драйвера из WDK и его модификации создателем нового драйвера.

Компания Microsoft также предоставляет верификатор для драйверов (driver verifier), который проверяет многие действия драйвера, чтобы обеспечить уверенность в том, что он соответствует требованиям WDM (по структуре и протоколам запросов вводавывода, управлению памятью и т. д.). Верификатор поставляется вместе с системой, администраторы могут запустить его командой verifier.exe, которая позволяет им указать, какие драйверы будут проверяться и насколько всесторонними (то есть дорогими) должны быть эти проверки.

При всей этой поддержке разработки и верификации драйверов в Windows попрежнему очень трудно написать даже простой драйвер, поэтому компания Microsoft создала систему оболочек под названием WDF (Windows Driver Foundation), которая работает поверх WDM и упрощает многие стандартные требования (в основном связанные с правильным взаимодействием с управлением электропитанием и операциями Plug-and-Play).

Чтобы еще больше упростить написание драйверов, а также повысить живучесть системы, WDF включает в себя инфраструктуру UMDF (User-Mode Driver Framework) для написания драйверов в виде выполняющихся в процессах служб. Существует также KMDF (Kernel-Mode Driver Framework) для написания драйверов как служб, выполняющихся в ядре, при этом многие подробности WDM реализуются автоматически. Поскольку в основе лежит WDM (с ее моделью драйверов), то именно на ней мы и сосредоточимся в этом разделе.

Устройства в Windows представлены объектами устройств. Объекты устройств используются для представления аппаратных средств (таких, как шины), а также как программные абстракции (наподобие файловых систем, сетевых протоколов и расширений ядра вроде антивирусных драйверов-фильтров). Все они формируют то, что Windows называет стеком устройств (как было показано на рис. 11.7).

Операции ввода-вывода инициируются диспетчером ввода-вывода, который вызывает интерфейс IoCallDriver исполнительного уровня с указателями на верхний объект устройства и на IRP (который представляет запрос ввода-вывода). Эта процедура находит объект драйвера, связанный с объектом устройства. Указанные в IRP типы операций обычно соответствуют описанным ранее системным вызовам диспетчера ввода-вывода, таким как CREATE, READ и CLOSE.

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



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




Поделитесь с Вашими друзьями:
  1   2   3


База данных защищена авторским правом ©nethash.ru 2017
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал