Поделиться через


Минидрайверы, минипорт драйверы и пары драйверов

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

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

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

схема, показывающая структуру объекта драйвера с элементом выгрузки.

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

схема, показывающая структуру драйвер-объект с компонентом majorfunction.

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
  • IRP_MJ_CLEANUP
  • IRP_MJ_POWER
  • IRP_MJ_SYSTEM_CONTROL
  • IRP_MJ_PNP

Остальные элементы MajorFunction массива содержат указатели на функцию отправки по умолчанию nt! IopInvalidDeviceRequest.

В выводе отладчика можно увидеть, что драйвер Parport предоставил указатели функций для Unload и AddDevice, но не предоставил указатель функции для StartIo. Функция AddDevice является необычной, так как ее указатель функции не хранится в структуре DRIVER_OBJECT. Вместо этого он хранится в члене расширения к структуре DRIVER_OBJECT в AddDevice. На следующей схеме показаны указатели на функции, предоставляемые драйвером парпорта в функции DriverEntry. Указатели функции, предоставляемые парпортом, затеняются.

схему указателей функций в структуре объекта драйвера.

Упрощение использования пар драйверов

В течение некоторого времени, как разработчики драйверов внутри и за пределами Майкрософт получили опыт работы с моделью драйвера Windows (WDM), они поняли несколько вещей о функциях диспетчеризации:

  • Функции диспетчеризации в значительной степени являются стандартными. Например, большая часть кода в функции диспетчеризации для 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, показывающая три объекта устройства в стеке устройств: afterthought.sys (фильтр do), prosewarerobot.sys, generalrobot.sys (ФДО) и pci.sys (ПДО).

Как показано на предыдущей схеме, стек устройств для Proseware Robot содержит три объекта устройства. Верхний объект устройства — это объект фильтр-устройства (Filter 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.

схема стека устройств беспроводной сетевой карты, показывающая netwlv64.sysи ndis.sys как парную связку драйверов, связанную с fdo, и pci.sys, связанную с pdo.

пары доступных драйверов

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

  • (драйвер минипорта дисплея, драйвер дисплейного порта)
  • (драйвер минипорта аудиоустройства, драйвер аудиопорта)
  • (драйвер минипорта хранилища, драйвер порта хранилища)
  • (драйвер миникласса батареи, драйвер класса батареи)
  • (Минидрайвер HID, класс-драйвер HID)
  • (драйвер миникласса для меняющегося класса, драйвер порта смены)
  • (минипортный драйвер NDIS, библиотека NDIS)

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

основные понятия для всех разработчиков драйверов

узлах устройств и стеках устройств

Стек драйверов