Dela via


Minidrivrutiner, Miniport-drivrutiner och drivrutinspar

En minidrivrutin eller en miniportdrivrutin fungerar som hälften av ett drivrutinpar. Ett drivrutinspar som till exempel (miniport, port) kan göra utvecklingen av drivrutiner enklare. I ett drivrutinspar hanterar en drivrutin allmänna uppgifter som är gemensamma för en hel samling enheter, medan den andra drivrutinen hanterar uppgifter som är specifika för en enskild enhet. De drivrutiner som hanterar enhetsspecifika uppgifter har en mängd olika namn, inklusive miniportdrivrutin, miniklassdrivrutin och minidrivrutin.

Microsoft tillhandahåller den allmänna drivrutinen, och vanligtvis tillhandahåller en oberoende maskinvaruleverantör den specifika drivrutinen. Innan du läser det här avsnittet bör du förstå de idéer som presenteras i Enhetsnoder och enhetsstackar och I/O-begärandepaket.

Varje drivrutin i kernelläge måste implementera en funktion med namnet DriverEntry, som anropas kort efter att drivrutinen har lästs in. Funktionen DriverEntry fyller i vissa element i en DRIVER_OBJECT struktur med pekare till flera andra funktioner som drivrutinen implementerar. Funktionen DriverEntry fyller till exempel i Unload-medlemmen i DRIVER_OBJECT-strukturen med en pekare till drivrutinens Unload-funktion, enligt följande diagram.

diagram som visar drivrutinsobjektets struktur med unload-medlemmen.

MajorFunction medlem i DRIVER_OBJECT-strukturen är en matris med pekare till funktioner som hanterar I/O-begärandepaket (IRP-s), enligt följande diagram. Vanligtvis fyller drivrutinen i flera medlemmar i MajorFunction-fältet med pekare till funktioner (implementerade av drivrutinen) som hanterar olika typer av IRP:er.

diagram som visar strukturen driver-object med majorfunction-medlemmen.

En IRP kan kategoriseras enligt dess huvudfunktionskod, som identifieras av en konstant, till exempel IRP_MJ_READ, IRP_MJ_WRITEeller IRP_MJ_PNP. Konstanterna som identifierar viktig funktionskod fungerar som index i matrisen MajorFunction. Anta till exempel att drivrutinen implementerar en dispatch-funktion för att hantera IRP:er som har huvudfunktionskoden IRP_MJ_WRITE. I det här fallet måste drivrutinen fylla i elementet MajorFunction[IRP_MJ_WRITE] i matrisen med en pekare till dispatchfunktionen.

Vanligtvis fyller drivrutinen i några av elementen i matrisen MajorFunction och lämnar de återstående elementen inställda på standardvärden som tillhandahålls av I/O-chefen. I följande exempel visas hur du använder !drvobj debuggertillägg för att inspektera parport-drivrutinens funktionspekare.

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

I debuggerns utdata kan du se att parport.sys implementerar GsDriverEntry, inträdespunkten för drivrutinen. GsDriverEntry, som genererades automatiskt när drivrutinen skapades, utför viss initiering och anropar sedan DriverEntry, som implementerades av drivrutinsutvecklaren.

Du kan också se att parporteringsdrivrutinen (i dess DriverEntry-funktion) innehåller pekare för att skicka funktioner för dessa viktiga funktionskoder:

  • 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

De återstående elementen i MajorFunction matrisen innehåller pekare till dispatchstandardfunktionen nt!IopInvalidDeviceRequest.

I felsökningsprogrammets utdata kan du se att parportdrivrutinen gav funktionspekare för Avlasta och AddDevice, men inte gav en funktionspekare för StartIo. Funktionen AddDevice är ovanlig eftersom funktionspekaren inte lagras i strukturen DRIVER_OBJECT. I stället lagras den i AddDevice medlem i ett tillägg till DRIVER_OBJECT-strukturen. Följande diagram illustrerar de funktionspekare som parportdrivrutinen angav i funktionen DriverEntry. Funktionspekare som tillhandahålls av parport är skuggade.

diagram över funktionspekare i en struktur för drivrutinsobjekt.

Gör det enklare genom att använda förarpar

Under en tidsperiod, när drivrutinsutvecklare inom och utanför Microsoft fick erfarenhet av Windows Driver Model (WDM), insåg de ett par saker om dispatch-funktioner:

  • Dispatch-funktioner är till stor del standardkod. Till exempel är mycket av koden i funktionen dispatch för IRP_MJ_PNP densamma för alla drivrutiner. Det är bara en liten del av PnP-koden (Plug and Play) som är specifik för en enskild drivrutin som styr en enskild maskinvarudel.
  • Dispatch-funktioner är komplicerade och svåra att få rätt. Det är svårt att implementera funktioner som trådsynkronisering, IRP-köer och IRP-annullering och kräver en djup förståelse för hur operativsystemet fungerar.

För att göra det enklare för förarutvecklare skapade Microsoft flera teknikspecifika drivrutinsmodeller. Vid första anblicken verkar de teknikspecifika modellerna helt annorlunda än varandra, men en närmare titt visar att många av dem är baserade på detta paradigm:

  • Drivrutinen är uppdelad i två delar: en som hanterar den allmänna bearbetningen och en som hanterar bearbetning som är specifik för en viss enhet.
  • Den allmänna delen är skriven av Microsoft.
  • Den specifika delen kan vara skriven av Microsoft eller en oberoende maskinvaruleverantör.

Anta att både Proseware- och Contoso-företagen gör en leksaksrobot som kräver en WDM-drivrutin. Anta också att Microsoft tillhandahåller en allmän robotdrivrutin med namnet GeneralRobot.sys. Proseware och Contoso kan var och en skriva små drivrutiner som hanterar kraven för deras specifika robotar. Proseware kan till exempel skriva ProsewareRobot.sysoch de två drivrutinerna (ProsewareRobot.sys, GeneralRobot.sys) kan kombineras för att bilda en enda WDM-drivrutin. På samma sätt kan drivrutinsparet (ContosoRobot.sys, GeneralRobot.sys) kombineras för att bilda en enda WDM-drivrutin. I sin mest allmänna form är tanken att du kan skapa drivrutiner med hjälp av (specific.sys, general.sys) par.

Funktionspekare i drivpar

I ett par (specific.sys, general.sys) laddar Windows specific.sys och anropar DriverEntry-funktionen. Funktionen DriverEntry hos specific.sys tar emot en pekare till en DRIVER_OBJECT-struktur. Normalt kan du förvänta dig att DriverEntry fyller i flera element i MajorFunction-array med pekare till dispatch-funktioner. Du kan också förvänta dig att DriverEntry fyller i Unload-medlemmen (och eventuellt StartIo-medlemmen) i DRIVER_OBJECT-strukturen samt AddDevice-medlemmen i drivrutinsobjektets tillägg. Men i en modell med drivrutinspar behöver DriverEntry inte nödvändigtvis göra detta. Funktionen DriverEntry i specific.sys skickar i stället DRIVER_OBJECT-strukturen till en initieringsfunktion som implementeras av general.sys. I följande kodexempel visas hur initieringsfunktionen kan anropas i paret (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);
}

Initieringsfunktionen i GeneralRobot.sys skriver funktionspekare till lämpliga medlemmar i DRIVER_OBJECT-strukturen (och dess utvidgning) och lämpliga element i MajorFunction-matrisen. Tanken är att när I/O-hanteraren skickar en IRP till drivrutinsparet går IRP först till distributionsfunktionen som implementeras av GeneralRobot.sys. Om GeneralRobot.sys kan hantera IRP på egen hand behöver den specifika drivrutinen, ProsewareRobot.sys, inte vara inblandad. Om GeneralRobot.sys kan hantera vissa, men inte alla, av IRP-bearbetningen får den hjälp från en av återanropsfunktionerna som implementeras av ProsewareRobot.sys. GeneralRobot.sys tar emot pekare till callbacks för ProsewareRobot i GeneralRobotInit-anropet.

Vid någon tidpunkt efter att DriverEntry- returnerar, konstrueras en enhetsstack för Proseware Robot-enhetsnoden. Enhetsstacken kan se ut så här.

diagram över noden för proseware-robotenheten, som visar tre enhetsobjekt i enhetsstacken: afterthought.sys (filter do), prosewarerobot.sys, generalrobot.sys (fdo) och pci.sys (pdo).

Som du ser i föregående diagram har enhetsstacken för Proseware Robot tre enhetsobjekt. Det översta enhetsobjektet är ett filterenhetsobjekt (Filter DO) som är associerat med filterdrivrutinen AfterThought.sys. Det mellersta enhetsobjektet är ett funktionellt enhetsobjekt (FDO) som är associerat med drivrutinsparet (ProsewareRobot.sys, GeneralRobot.sys). Drivrutinsparet fungerar som funktionsdrivrutin för enhetsstacken. Det nedre enhetsobjektet är ett fysiskt enhetsobjekt (PDO) som är associerat med Pci.sys.

Observera att drivrutinsparet endast upptar en nivå i enhetsstacken och endast är associerat med ett enhetsobjekt: FDO. När GeneralRobot.sys bearbetar en IRP kan den anropa ProsewareRobot.sys för hjälp, men det är inte samma sak som att skicka begäran nedåt i enhetsstacken. Drivarparet bildar en enda WDM-drivrutin som befinner sig på en nivå i enhetsstacken. Drivrutinsparet slutför antingen IRP eller skickar den nedåt i enhetsstacken till PDO:n, som är associerad med Pci.sys.

Exempel på ett drivpar

Anta att du har ett trådlöst nätverkskort på den bärbara datorn, och genom att titta i Enhetshanteraren kan du fastställa att netwlv64.sys är drivrutinen för nätverkskortet. Du kan använda debuggerutvidgningen !drvobj för att granska funktionspekarna för 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

I felsökningsprogrammets utdata kan du se att netwlv64.sys implementerar GsDriverEntry, ingångspunkten för drivrutinen. GsDriverEntry, som genererades automatiskt när drivrutinen skapades, utför viss initiering och anropar sedan DriverEntry, som skrevs av drivrutinsutvecklaren.

I det här exemplet implementerar netwlv64.sys DriverEntry, men ndis.sys implementerar AddDevice, Unloadoch flera dispatch-funktioner. Netwlv64.sys kallas för en NDIS-miniportdrivrutin och ndis.sys kallas för NDIS-biblioteket. Tillsammans bildar de två modulerna ett (NDIS-miniport, NDIS-bibliotek) par.

Det här diagrammet visar enhetsstacken för det trådlösa nätverkskortet. Observera att drivrutinsparet (netwlv64.sys, ndis.sys) endast upptar en nivå i enhetsstacken och endast är associerat med ett enhetsobjekt: FDO.

diagram över enhetsstacken för trådlöst nätverkskort som visar netwlv64.sys, ndis.sys som drivrutinsparet som är associerat med fdo och pci.sys som är associerad med den pdo.

Tillgängliga drivrutinpar

De olika teknikspecifika drivrutinsmodellerna använder en mängd olika namn för de specifika och allmänna delarna i ett drivrutinspar. I många fall har den specifika delen av paret prefixet "mini". Här är några av (specifika, allmänna) par som är tillgängliga:

  • (visa miniportdrivrutin, visa portdrivrutin)
  • (ljudminiportdrivrutin, ljudportdrivrutin)
  • (miniportdrivrutin för lagring, lagringsportdrivrutin)
  • (batteri miniklass drivrutin, batteriklass drivrutin)
  • (HID-minidrivrutin, HID-klassdrivrutin)
  • (ändringsdrivrutin för miniklass, växelportdrivrutin)
  • (NDIS-miniportdrivrutin, NDIS-bibliotek)

Obs Som du kan se i listan, använder flera av modellerna termen klassdrivrutin för den allmänna delen av ett drivrutinspar. Den här typen av klassdrivrutin skiljer sig från en fristående klassdrivrutin och skiljer sig från en klassfilterdrivrutin.

Begrepp för alla drivrutinsutvecklare

Enhetsnoder och enhetsstackar

drivrutinsstackar