Enhetsnoder och enhetsstackar
I Windows representeras enheterna av enhetsnoder i enhetsträdet Plug and Play (PnP). När en I/O-begäran skickas till en enhet kan flera drivrutiner hantera begäran. Var och en av dessa drivrutiner är associerad med ett enhetsobjekt och enhetsobjekten ordnas i en stack. Sekvensen av enhetsobjekt tillsammans med deras associerade drivrutiner kallas för en enhetsstack. Varje enhetsnod har en egen enhetsstack.
Enhetsnoder och Plug and Play-enhetsträdet
Windows organiserar enheter i en trädstruktur som kallas Plug and Play-enhetsträdet, eller helt enkelt enhetsträdet. Vanligtvis representerar en nod i enhetsträdet antingen en enhet eller en enskild funktion på en sammansatt enhet. Vissa noder representerar dock programvarukomponenter som inte har någon koppling till fysiska enheter.
En nod i enhetsträdet kallas för en enhetsnod. Rotnoden i enhetsträdet kallas rotenhetsnoden. Enligt konventionen ritas rotenhetsnoden längst ned i enhetsträdet, enligt följande diagram.
Enhetsträdet illustrerar de överordnade/underordnade relationer som är inneboende i PnP-miljön. Flera av noderna i enhetsträdet representerar bussar som har anknytningsenheter anslutna till dem. PcI Bus-noden representerar till exempel den fysiska PCI-bussen på moderkortet. Under starten ber PnP-chefen PCI-bussdrivrutinen att räkna upp de enheter som är anslutna till PCI-bussen. Dessa enheter representeras av underordnade noder i PCI Bus-noden. I det föregående diagrammet har PCI Bus-noden underordnade noder för flera enheter som är anslutna till PCI-bussen, såsom USB-värdstyrenheter, en ljudstyrenhet och en PCI Express-port.
Några av de enheter som är anslutna till PCI-bussen är själva bussarna. PnP-chefen ber var och en av dessa bussar att räkna upp de enheter som är anslutna till den. I föregående diagram kan vi se att ljudstyrenheten är en buss som har en ljudenhet ansluten till den. Vi kan se att PCI Express-porten är en buss som har ett bildskärmskort anslutet till sig och att bildskärmskortet är en buss som har en bildskärm ansluten till sig.
Om du tänker på en nod som representerar en enhet eller en buss beror på din synvinkel. Du kan till exempel se bildskärmskortet som en enhet som spelar en viktig roll när det gäller att förbereda bildrutor som visas på skärmen. Men du kan också se bildskärmskortet som en buss som kan identifiera och räkna upp anslutna bildskärmar.
Enhetsobjekt och enhetsstackar
Ett enhetsobjekt är en instans av en DEVICE_OBJECT struktur. Varje enhetsnod i PnP-enhetsträdet har en ordnad lista över enhetsobjekt och vart och ett av dessa enhetsobjekt är associerat med en drivrutin. Den ordnade listan över enhetsobjekt, tillsammans med deras associerade drivrutiner, kallas enhetsstacken för enhetsnoden.
Du kan komma på en enhetsstack på flera sätt. I den mest formella bemärkelsen är en enhetsstack en ordnad lista över par (enhetsobjekt, drivrutin). I vissa sammanhang kan det dock vara bra att tänka på enhetsstacken som en ordnad lista över enhetsobjekt. I andra sammanhang kan det vara bra att tänka på enhetsstacken som en ordnad lista över drivrutiner.
Enligt konventionen har en enhetsstack en topp och en nederkant. Det första enhetsobjektet som skapas i enhetsstacken finns längst ned och det sista enhetsobjektet som ska skapas och kopplas till enhetsstacken finns längst upp.
I följande diagram har Proseware Gizmo-enhetsnoden enhetsstack som innehåller tre (enhetsobjekt, drivrutin) par. Det översta enhetsobjektet är associerat med drivrutinen AfterThought.sys, det mellersta enhetsobjektet är associerat med drivrutinen Proseware.sysoch det nedre enhetsobjektet är associerat med drivrutinen Pci.sys. PCI Bus-noden i mitten av diagrammet har en enhetsstack som innehåller två (enhetsobjekt, drivrutinspar) – ett enhetsobjekt som är associerat med Pci.sys och ett enhetsobjekt som är associerat med Acpi.sys.
Hur byggs en enhetsstack upp?
Under uppstart begär PnP-hanteraren att drivrutinen för varje buss ska räkna upp underordnade enheter som är anslutna till bussen. PnP-chefen ber till exempel PCI-busschauffören (Pci.sys) att räkna upp de enheter som är anslutna till PCI-bussen. Som svar på den här begäran skapar Pci.sys ett enhetsobjekt för varje enhet som är ansluten till PCI-bussen. Vart och ett av dessa enhetsobjekt kallas för ett fysiskt enhetsobjekt (PDO). Kort efter att Pci.sys har skapat uppsättningen pdos ser enhetsträdet ut som det som visas i följande diagram.
PnP-hanteraren associerar en enhetsnod med varje nyskapade PDO och söker i registret för att avgöra vilka drivrutiner som måste ingå i enhetsstacken för noden. Enhetsstacken måste ha en (och bara en) funktionsdrivrutin och kan valfritt ha en eller flera filterdrivrutiner. Funktionsdrivrutinen är den huvudsakliga drivrutinen för enhetsstacken och ansvarar för att hantera läs-, skriv- och enhetskontrollbegäranden. Filterdrivrutiner spelar hjälproller vid bearbetning av läs-, skriv- och enhetskontrollbegäranden. När varje funktions- och filterdrivrutin läses in skapar den ett enhetsobjekt och kopplar sig till enhetsstacken. Ett enhetsobjekt som skapas av funktionsdrivrutinen kallas för ett funktionellt enhetsobjekt (FDO) och ett enhetsobjekt som skapats av en filterdrivrutin kallas filtrera enhetsobjekt (Filter DO). Nu ser enhetsträdet ut ungefär som det här diagrammet.
I diagrammet ser du att filterdrivrutinen i en nod ligger ovanför funktionsdrivrutinen, och i den andra noden ligger filterdrivrutinen under funktionsdrivrutinen. En filterdrivrutin ovanför funktionsdrivrutinen i en enhetsstack kallas för en övre filterdrivrutin. En filterdrivrutin som ligger under funktionsdrivrutinen kallas för en lägre filterdrivrutin.
PDO:n är alltid det nedre enhetsobjektet i en enhetsstacken. Detta beror på hur en enhetsstack konstrueras. PDO skapas först och när ytterligare enhetsobjekt är anslutna till stacken är de anslutna till den befintliga stackens överkant.
Obs När drivrutinerna för en enhet installeras använder installationsprogrammet information i en inf-fil (information) för att avgöra vilken drivrutin som är funktionsdrivrutinen och vilka drivrutiner som är filter. Vanligtvis tillhandahålls INF-filen antingen av Microsoft eller av maskinvaruleverantören. När drivrutinerna för en enhet har installerats kan PnP-hanteraren fastställa enhetens funktions- och filterdrivrutiner genom att titta i registret.
Busschaufförer
I föregående diagram kan du se att drivrutinen Pci.sys spelar två roller. Först är Pci.sys associerad med FDO:n i PCI Bus-enhetsnoden. I själva verket skapade den FDO:n i PCI Bus-enhetsnoden. Så Pci.sys är funktionsdrivrutinen för PCI-bussen. För det andra är Pci.sys associerat med PDO i varje underordnad PCI Bus-nod. Kom ihåg att den skapade PDOs för de underordnade enheterna. Drivrutinen som skapar PDO:n för en enhetsnod kallas bussdrivrutin för noden.
Om din referenspunkt är PCI-bussen är Pci.sys funktionsdrivrutinen. Men om din referenspunkt är Proseware Gizmo-enheten är Pci.sys busschauffören. Den här dubbla rollen är typisk i PnP-enhetsträdet. En förare som fungerar som funktionsdrivrutin för en buss fungerar också som busschaufför för en underordnad enhet i bussen.
Enhetsstackar i användarläge
Hittills har vi diskuterat enhetsstackar i kärnläge. Det vill: drivrutinerna i stackarna körs i kernelläge och enhetsobjekten mappas till systemutrymmet, vilket är det adressutrymme som endast är tillgängligt för kod som körs i kernelläge. Information om skillnaden mellan kernelläge och användarläge finns i Användarläge och kernelläge.
I vissa fall har en enhet en enhetsstack i användarläge utöver enhetsstacken i kernelläge. Drivrutiner i användarläge baseras ofta på User-Mode Driver Framework (UMDF), som är en av drivrutinsmodellerna som tillhandahålls av Windows Driver Frameworks (WDF). I UMDF är drivrutinerna DLL:er i användarläge och enhetsobjekten är COM-objekt som implementerar IWDFDevice-gränssnittet. Ett enhetsobjekt i en UMDF-enhetsstack kallas WDF-enhetsobjekt (WDF DO).
Följande diagram visar enhetsnoden, enhetsstacken i kernelläge och enhetsstacken i användarläge för en USB-FX-2-enhet. Drivrutinerna i både användarläges- och kernellägesstackarna deltar i I/O-förfrågningar som riktas mot USB-FX-2-enheten.