Мини-накопители, драйверы 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, показывающая три объекта устройства в стеке устройств: afterthought.sys (фильтрация), prosewarerobot.sys, generalrobot.sys (fdo) и pci.sys (pdo).

Как показано на предыдущей схеме, стек устройств для 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.

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

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

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

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

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

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

Узлы устройств и стеки устройств

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