Мини-накопители, драйверы miniport и пары драйверов
Мини-диск или драйвер мини-порта выступает в качестве половины пары драйверов. Пары драйверов, такие как (минипорт, порт), могут упростить разработку драйверов. В паре драйверов один драйвер обрабатывает общие задачи, общие для всей коллекции устройств, а другой — задачи, относящиеся к конкретному устройству. Драйверы, которые выполняют задачи, связанные с устройством, идут по различным именам, включая драйвер мини-порта, драйвер мини-класса и мини-диск.
Корпорация Майкрософт предоставляет общий драйвер, а как правило, независимый поставщик оборудования предоставляет конкретный драйвер. Прежде чем читать этот раздел, необходимо понять идеи, представленные в разделах Узлы устройств и стеки устройств и пакеты запросов ввода-вывода.
Каждый драйвер режима ядра должен реализовывать функцию с именем DriverEntry, которая вызывается вскоре после загрузки драйвера. Функция DriverEntry заполняет определенные элементы структуры DRIVER_OBJECT указателями на несколько других функций, которые реализует драйвер. Например, функция DriverEntry заполняет элемент Unload структуры DRIVER_OBJECT указателем на функцию Unload драйвера, как показано на следующей схеме.
Элемент MajorFunction структуры DRIVER_OBJECT представляет собой массив указателей на функции, обрабатывающие пакеты запросов ввода-вывода (IRP), как показано на следующей схеме. Как правило, драйвер заполняет несколько элементов массива MajorFunction указателями на функции (реализованные драйвером), которые обрабатывают различные виды IRP.
IRP можно классифицировать в соответствии с основным кодом функции, который определяется константой, такой как IRP_MJ_READ, IRP_MJ_WRITE или IRP_MJ_PNP. Константы, идентифицирующие код основной функции, служат индексами в массиве MajorFunction . Например, предположим, что драйвер реализует функцию диспетчеризации для обработки IRP, имеющих основной код функции IRP_MJ_WRITE. В этом случае драйвер должен заполнить элемент MajorFunction[IRP_MJ_WRITE] массива указателем на функцию диспетчеризации.
Как правило, драйвер заполняет некоторые элементы массива MajorFunction и оставляет остальным элементам значения по умолчанию, предоставленные диспетчером ввода-вывода. В следующем примере показано, как использовать расширение отладчика !drvobj для проверки указателей функций для драйвера parport.
0: kd> !drvobj parport 2
Driver object (fffffa80048d9e70) is for:
\Driver\Parport
DriverEntry: fffff880065ea070 parport!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff880065e131c parport!PptUnload
AddDevice: fffff880065d2008 parport!P5AddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880065d49d0 parport!PptDispatchCreateOpen
[01] IRP_MJ_CREATE_NAMED_PIPE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE fffff880065d4a78 parport!PptDispatchClose
[03] IRP_MJ_READ fffff880065d4bac parport!PptDispatchRead
[04] IRP_MJ_WRITE fffff880065d4bac parport!PptDispatchRead
[05] IRP_MJ_QUERY_INFORMATION fffff880065d4c40 parport!PptDispatchQueryInformation
[06] IRP_MJ_SET_INFORMATION fffff880065d4ce4 parport!PptDispatchSetInformation
[07] IRP_MJ_QUERY_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL fffff880065d4be8 parport!PptDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880065d4c24 parport!PptDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP fffff880065d4af4 parport!PptDispatchCleanup
[13] IRP_MJ_CREATE_MAILSLOT fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER fffff880065d491c parport!PptDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL fffff880065d4d4c parport!PptDispatchSystemControl
[18] IRP_MJ_DEVICE_CHANGE fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA fffff80001b6ecd4 nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP fffff880065d4840 parport!PptDispatchPnp
В выходных данных отладчика видно, что parport.sys реализует GsDriverEntry, точку входа для драйвера. GsDriverEntry, который был создан автоматически при сборке драйвера, выполняет некоторую инициализацию, а затем вызывает DriverEntry, который был реализован разработчиком драйвера.
Вы также можете видеть, что драйвер parport (в его функции DriverEntry ) предоставляет указатели на функции диспетчеризации для следующих основных кодов функций:
- IRP_MJ_CREATE
- IRP_MJ_CLOSE
- IRP_MJ_READ
- IRP_MJ_WRITE
- IRP_MJ_QUERY_INFORMATION
- IRP_MJ_SET_INFORMATION
- IRP_MJ_DEVICE_CONTROL
- IRP_MJ_INTERNAL_DEVICE_CONTROL
- IRP_MJ_CLEANUP
- IRP_MJ_POWER
- IRP_MJ_SYSTEM_CONTROL
- IRP_MJ_PNP
Остальные элементы массива MajorFunction содержат указатели на функцию диспетчеризации по умолчанию nt! IopInvalidDeviceRequest.
В выходных данных отладчика видно, что драйвер parport предоставил указатели функций для Unload и AddDevice, но не предоставил указатель функции для StartIo. Функция AddDevice является необычной, так как ее указатель функции не хранится в структуре DRIVER_OBJECT . Вместо этого он хранится в элементе AddDevice расширения для структуры DRIVER_OBJECT . На следующей схеме показаны указатели функций, предоставленные драйвером parport в функции DriverEntry . Указатели функций, предоставляемые parport, затеняются.
Упрощение работы с помощью пар драйверов
В течение некоторого времени, когда разработчики драйверов в корпорации Майкрософт и за ее пределами приобрели опыт работы с моделью драйвера Windows (WDM), они поняли несколько вещей о функциях диспетчеризации:
- Функции диспетчеризации в значительной степени являются стандартными. Например, большая часть кода в функции dispatch для IRP_MJ_PNP одинакова для всех драйверов. Это лишь небольшая часть кода Plug and Play (PnP), относящаяся к отдельному драйверу, который управляет отдельным элементом оборудования.
- Функции диспетчеризации являются сложными и трудными для правильного выполнения. Реализация таких функций, как синхронизация потоков, постановка в очередь IRP и отмена IRP, является сложной задачей и требует глубокого понимания того, как работает операционная система.
Чтобы упростить разработку драйверов, корпорация Майкрософт создала несколько моделей драйверов, относящихся к конкретной технологии. На первый взгляд, модели, связанные с технологиями, кажутся совершенно отличными друг от друга, но более подробный взгляд показывает, что многие из них основаны на этой парадигме:
- Драйвер делится на две части: один обрабатывает общую обработку, а второй — обработку, относяющуюся к конкретному устройству.
- Общая часть написана корпорацией Майкрософт.
- Конкретный фрагмент может быть написан корпорацией Майкрософт или независимым поставщиком оборудования.
Предположим, что компании Proseware и Contoso делают робота-игрушку, которому требуется драйвер WDM. Кроме того, предположим, что корпорация Майкрософт предоставляет общий драйвер робота под названием GeneralRobot.sys. Proseware и Contoso могут писать небольшие драйверы, которые обрабатывают требования конкретных роботов. Например, Proseware может записывать ProsewareRobot.sys, а пару драйверов (ProsewareRobot.sys, GeneralRobot.sys) можно объединить для формирования одного драйвера WDM. Аналогичным образом, пара драйверов (ContosoRobot.sys, GeneralRobot.sys) может объединяться в единый драйвер WDM. В самом общем виде идея заключается в том, что вы можете создавать драйверы с помощью пар (specific.sys, general.sys).
Указатели функций в парах драйверов
В паре (specific.sys, general.sys) Windows загружает specific.sys и вызывает ее функцию DriverEntry . Функция DriverEntry specific.sys получает указатель на структуру DRIVER_OBJECT . Обычно ожидается, что DriverEntry заполняет несколько элементов массива MajorFunction указателями на функции диспетчеризации. Кроме того, следует ожидать, что DriverEntry заполняет элемент Unload (и, возможно, элемент StartIo ) структуры DRIVER_OBJECT и элемент AddDevice расширения объекта драйвера. Однако в модели пары драйверов DriverEntry не обязательно делает это. Вместо этого функция DriverEntry specific.sys передает структуру DRIVER_OBJECT вместе с функцией инициализации, реализованной general.sys. В следующем примере кода показано, как функция инициализации может вызываться в паре (ProsewareRobot.sys, GeneralRobot.sys).
PVOID g_ProsewareRobottCallbacks[3] = {DeviceControlCallback, PnpCallback, PowerCallback};
// DriverEntry function in ProsewareRobot.sys
NTSTATUS DriverEntry (DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
// Call the initialization function implemented by GeneralRobot.sys.
return GeneralRobotInit(DriverObject, RegistryPath, g_ProsewareRobottCallbacks);
}
Функция инициализации в GeneralRobot.sys записывает указатели функции на соответствующие элементы структуры DRIVER_OBJECT (и ее расширения) и соответствующие элементы массива MajorFunction . Идея заключается в том, что, когда диспетчер ввода-вывода отправляет IRP паре драйверов, IRP сначала переходит к функции диспетчеризации, реализованной GeneralRobot.sys. Если GeneralRobot.sys может обрабатывать IRP самостоятельно, то конкретный драйвер, ProsewareRobot.sys, не должен быть задействован. Если GeneralRobot.sys может обрабатывать некоторые, но не все, обработку IRP, она получает помощь от одной из функций обратного вызова, реализованных ProsewareRobot.sys. GeneralRobot.sys получает указатели на обратные вызовы ProsewareRobot в вызове GeneralRobotInit.
В какой-то момент после возврата DriverEntry создается стек устройств для узла устройства Proseware Robot. Стек устройств может выглядеть следующим образом.
Как показано на предыдущей схеме, стек устройств для Proseware Robot содержит три объекта устройства. Верхний объект устройства — это объект устройства фильтра (Фильтр DO), связанный с драйвером фильтра AfterThought.sys. Средний объект устройства — это объект функционального устройства (FDO), связанный с парой драйверов (ProsewareRobot.sys, GeneralRobot.sys). Пара драйверов служит драйвером функции для стека устройств. Нижний объект устройства — это объект физического устройства (PDO), связанный с Pci.sys.
Обратите внимание, что пара драйверов занимает только один уровень в стеке устройств и связана только с одним объектом устройства: FDO. Когда GeneralRobot.sys обрабатывает IRP, он может вызвать ProsewareRobot.sys за помощью, но это не то же самое, что передача запроса в стек устройств. Пара драйверов формирует один драйвер WDM, который находится на одном уровне в стеке устройств. Пара драйверов завершает IRP или передает его вниз по стеку устройств в PDO, который связан с Pci.sys.
Пример пары драйверов
Предположим, что у вас есть карта беспроводной сети на ноутбуке, и, просмотрев диспетчер устройств, вы определите, что netwlv64.sys является драйвером для сетевого карта. Расширение отладчика !drvobj можно использовать для проверки указателей функций для netwlv64.sys.
1: kd> !drvobj netwlv64 2
Driver object (fffffa8002e5f420) is for:
\Driver\netwlv64
DriverEntry: fffff8800482f064 netwlv64!GsDriverEntry
DriverStartIo: 00000000
DriverUnload: fffff8800195c5f4 ndis!ndisMUnloadEx
AddDevice: fffff88001940d30 ndis!ndisPnPAddDevice
Dispatch routines:
[00] IRP_MJ_CREATE fffff880018b5530 ndis!ndisCreateIrpHandler
[01] IRP_MJ_CREATE_NAMED_PIPE fffff88001936f00 ndis!ndisDummyIrpHandler
[02] IRP_MJ_CLOSE fffff880018b5870 ndis!ndisCloseIrpHandler
[03] IRP_MJ_READ fffff88001936f00 ndis!ndisDummyIrpHandler
[04] IRP_MJ_WRITE fffff88001936f00 ndis!ndisDummyIrpHandler
[05] IRP_MJ_QUERY_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[06] IRP_MJ_SET_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[07] IRP_MJ_QUERY_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[08] IRP_MJ_SET_EA fffff88001936f00 ndis!ndisDummyIrpHandler
[09] IRP_MJ_FLUSH_BUFFERS fffff88001936f00 ndis!ndisDummyIrpHandler
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0b] IRP_MJ_SET_VOLUME_INFORMATION fffff88001936f00 ndis!ndisDummyIrpHandler
[0c] IRP_MJ_DIRECTORY_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0d] IRP_MJ_FILE_SYSTEM_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[0e] IRP_MJ_DEVICE_CONTROL fffff8800193696c ndis!ndisDeviceControlIrpHandler
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL fffff880018f9114 ndis!ndisDeviceInternalIrpDispatch
[10] IRP_MJ_SHUTDOWN fffff88001936f00 ndis!ndisDummyIrpHandler
[11] IRP_MJ_LOCK_CONTROL fffff88001936f00 ndis!ndisDummyIrpHandler
[12] IRP_MJ_CLEANUP fffff88001936f00 ndis!ndisDummyIrpHandler
[13] IRP_MJ_CREATE_MAILSLOT fffff88001936f00 ndis!ndisDummyIrpHandler
[14] IRP_MJ_QUERY_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[15] IRP_MJ_SET_SECURITY fffff88001936f00 ndis!ndisDummyIrpHandler
[16] IRP_MJ_POWER fffff880018c35e8 ndis!ndisPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL fffff880019392c8 ndis!ndisWMIDispatch
[18] IRP_MJ_DEVICE_CHANGE fffff88001936f00 ndis!ndisDummyIrpHandler
[19] IRP_MJ_QUERY_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1a] IRP_MJ_SET_QUOTA fffff88001936f00 ndis!ndisDummyIrpHandler
[1b] IRP_MJ_PNP fffff8800193e518 ndis!ndisPnPDispatch
В выходных данных отладчика видно, что netwlv64.sys реализует GsDriverEntry, точку входа для драйвера. GsDriverEntry, который был автоматически создан при сборке драйвера, выполняет некоторую инициализацию, а затем вызывает DriverEntry, написанный разработчиком драйвера.
В этом примере netwlv64.sys реализует DriverEntry, но ndis.sys реализует AddDevice, Unload и несколько функций диспетчеризации. Netwlv64.sys называется драйвером мини-порта NDIS, а ndis.sys — библиотекой NDIS. Вместе эти два модуля образуют пару (минипорт NDIS, библиотека NDIS).
На этой схеме показан стек устройств для карта беспроводной сети. Обратите внимание, что пара драйверов (netwlv64.sys, ndis.sys) занимает только один уровень в стеке устройств и связана только с одним объектом устройства: FDO.
Доступные пары драйверов
В различных моделях драйверов, зависящих от технологий, используются различные имена для конкретных и общих элементов пары драйверов. Во многих случаях определенная часть пары имеет префикс "мини". Ниже приведены некоторые из доступных (конкретных, общих) пар:
- (драйвер минипорта дисплея, драйвер порта дисплея)
- (драйвер аудиопорта, драйвер аудиопорта)
- (драйвер минипорта хранилища, драйвер порта хранилища)
- (драйвер мини-класса батареи, драйвер класса батареи)
- (мини-драйвер HID, драйвер класса HID)
- (драйвер мини-класса, драйвер порта устройства смены)
- (драйвер минипорта NDIS, библиотека NDIS)
Примечание Как видно из списка, в некоторых моделях используется термин драйвер класса для общей части пары драйверов. Этот тип драйвера класса отличается от автономного драйвера класса и отличается от драйвера фильтра класса.
Связанные темы
Основные понятия для всех разработчиков драйверов