Объекты для управления оборудованиемКак было упомянуто выше, объект устройства управляет работой устройства при помощи специальных объектов, управляющих работой оборудования - портами В/В, прерываниями, памятью, контроллерами ПДП. Драйвер создает эти объекты для представления физических параметров устройства. Большинство периферийных устройств находятся на шинах компьютера. В современном компьютере есть несколько шин. Обычно процессор, внешняя кэш-память, и оперативная память находятся на высокоскоростной шине, архитектура которой специфична для данного типа процессора. Шина процессора соединена мостом со стандартной скоростной шиной, на которой находятся контроллеры дисплея, некоторые скоростные устройства. Архитектура этой шины может быть процессоро-независимой. Пример такой шины - PCI. Эта шина также может быть соединена мостом со вторичной локальной шиной, часто более медленной. На ней могут находиться контроллеры дисковых накопителей, сетевых адаптеров и т.п. Периферийные устройства обычно имеют "на борту" регистры и диапазоны адресов памяти, при помощи которых реализуется интерфейс устройства с системой. Но добраться до них не так просто: процессор ведь физически использует другие механизмы для обращения к своим "родным" портам ввода-вывода и оперативной памяти. Для того, чтобы обратится к памяти и портам устройства, находящегося на локальной шине, процессор должен выполнить отображение (mapping) адресного пространства процессора и той шины, где находится наше устройство. В результате этой операции к участку памяти, физически находящийся в устройтсве, можно обращаться, как к участку оперативной памяти процессора. При таком обращении процессор переадресует запрос локальной шине. Но тут следует вспомнить об особенностях архитектуры Windows (да и практически любой современной ОС): ведь система поддерживает механизм виртуальной памяти! Пользовательские приложения теперь работают в своем адресном пространстве, а система, в том числе и драйвера, - в своем. Куда же будет отображена память устройства? Ответ прост. Можно отобразить диапазон адресов устройства как на адресное пространство системы, так и на адресное пространство пользовательского процесса. Соответственно различаться будет и способ доступа к памяти устройства из приложения пользователя: в первом случае буфер с данными для записи или чтения будет передаваться драйверу из приложения, а в драйвере эти данные будут пересылаться устройству. Во втором случае приложение будет писать и читать данные в выделенный ему участок памяти, который находится в адресном пространстве процесса. Какой механизм выбрать - дело разработчика драйвера. Объекты, представляющие адресное пространство периферийных устройств, представлены классами KPeripherialAdress, KIoRange, KMemoryRange, KIoregister, KMemoryRegister. KPeripherialAdress является базовым классом для большинства остальных классов управления диапазонами памяти и портов ввода-вывода. Сам класс KperipherialAdress в основном, не используется. Используются, в основном, следующие его подклассы:
Стоит отметить, что немалая часть устройств могут общаються со своей памятью только словами. Длина слова зависит от устройства, и может колебаться в широких пределах. Обычно для PCI-устройств - 32 бит. В документации настоятельно рекомендуется использовать только эти классы для управления оборудованием. Это связано с возможной переносимостью драйвера на другие платформы. При использовании этих классов, которые, в свою очередь, используют функции DDK для доступа к оборудованию, процесс портирования пройдет безболезненно, т.к. для доступа к устройству будет использован HAL. Если же программист будет пытаться управлять устройствами самостоятельно, то драйвер придется переписывать при переносе на другую платформу. Есть еще одна причина, по которой стоит использовать эти классы: ведь с ними разрабатывать драйвер намного проще!
Объекты синхронизации Как и все Windows - программы, драйвера являются частью многозадачной операционной системы, в которой выполняется множество процессов и потоков. Драйвер, как и программа, также может содержать несколько потоков. При этом, естественно, возникает проблема синхронизации работы этих потоков, совместного доступа к данным и т.п. Особенно актуальной эта проблема становится в многопроцессорной системе. Windows 2000 предназначается для работы в многопроцессорных системах, и если пренебречь синхронизацией при разработке драйвера, то это может повлечь за собой неприятные последствия. Для решения задач синхронизации WDM (и, соответственно, DriverWorks) предлагает различные средства. Простейшим из объектов синхронизации является защелка (Spin Lock), представленная классом KSpinLock. Принцип действия защелки очень прост: чтобы запретить любому другому потоку в системе доступ к данным, нужно вызывать метод Lock защелки. Любой поток, пытающийся получить доступ к заблокированным данным, уснет. Чтобы снять блокировку, нужно вызвать метод Unlock. Класс диспетчера KDispatcherObject является суперклассом для нескольких важных классов синхронизации. Эти классы управляют планировщиком Windows и позволяют синхронизировать как работу драйверов, так и работу приложения пользователя и драйвера. Все классы, порожденные от KDispatcherObject, имеют два важных отличия:
При работе с объектами диспетчера и его подклассов следует иметь в виду, что нельзя блокировать поток при обработке прерывания. Последствия будут фатальными. Подклассы класса KDispatcherObject: KEvent - используется для синхронизации работы потоков. Kevent почти не отличается от объекта диспетчера. KSemaphore инкапсулирует системный объект семафора. Семафор отличается от объекта события тем, что имеет счетчик. Семафор сигнализирует в том случае, если счетчик больше нуля. Семафоры могут быть полезны, например, при управлении несколькими пото- ками. KTimer - таймер. При создании таймера его флажок находится в состоянии "молчит". Временной интервал таймера задается функцией Set с точностью до 100 нс. На практике таймер устойчиво работает с временем ожидания >= 10 мс. Когда пройдет указанный промежуток времени, таймер перейдет в состояние "сигнализирует". Подклассом Ktimer является класс KTimedCallBack. В нем по истечении промежутка времени выполняется вызов отложенной процедуры (DPC). KSystemThread позволяет создать новый поток в драйвере. Потоки в драйвере используются в разных целях. В основном это - поллинг медленных устройств и работа на многопроцессорных системах. Для запуска потока следует создать функцию, которая станет функцией потока и вызвать метод Start. Для уничтожения потока - метод Terminate. При работе с потоками можно использовать все упомянутые выше классы синхронизации.
Дополнительные классы.
DriverWorks предоставляет дополнительные классы для нужд программиста. Это классы очередей, списков, стеков; классы файлов и Unicode- строк; классы синхронизации. Списки представлены класами KList, KInterlockedList, KInterruptSafeList. Они представляют шаблоны двунаправленных списков и стандартные методы для вставки, удаления и добавления элементов. Различаются эти классы методами синхронизации. KList не содержит никаких методов синхронизации и защиты данных. KInterLockedList использует защелки (spin locks) для защиты внутренних связей в списке. KInterruptSafeList использует присоединенный объект прерывания для защиты связей. По аналогичному принципу работают шаблоны классов FIFO (стек): KFifo, KLockableFifo, KInterruptSafeFifo. Класс KFile инкапсулирует методы для работы с файлами. Этот класс позволяет читать и записывать данные в файл а также изменять атрибуты файлов. Для представления Unicode - строк используется класс KUstring. Методы данного класса позволяют выполнять сравнение, конкатенацию, доступ к символам строки и разнообразные преобразования типа. |