Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Úvod
Tyto pokyny pro vývoj ovladačů byly vyvinuty v průběhu mnoha let vývojáři ovladačů ve společnosti Microsoft. V průběhu času, kdy se řidiči chovali nedisciplinovaně a byly z toho vyvozeny ponaučení, byla tato ponaučení zaznamenána a postupně rozvinuta do této sady pokynů. Tyto osvědčené postupy používá tým Microsoft Surface Hardware k vývoji a údržbě kódu ovladače zařízení, který podporuje jedinečné hardwarové prostředí Surface.
Stejně jako jakákoli sada pokynů budou existovat legitimní výjimky a alternativní přístupy, které budou stejně platné. Zvažte zahrnutí těchto pokynů do vašich vývojových standardů nebo jejich použití k zahájení specifických pokynů pro vaše vývojové prostředí a vaše jedinečné požadavky.
Běžné chyby provedené vývojáři ovladačů
Zpracování vstupně-výstupních operací
- Přístup k vyrovnávacím pamětím získaným z IOCTLs bez ověření délky. Viz Selhání kontroly velikosti vyrovnávacích pamětí.
- Provádění blokujících vstupně-výstupních operací v kontextu uživatelského vlákna nebo libovolného kontextu vlákna Viz Úvod k objektům dispečera jádra.
- Odesílání synchronních vstupně-výstupních operací jinému ovladači bez časového limitu Viz Synchronní odesílání I/O požadavků.
- Používání neither-IO IOCTLs bez pochopení jejich dopadů na zabezpečení. Viz bez vyrovnávací paměti ani přímého vstupně-výstupního.
- Nekontrolování stavu vrácení WdfRequestForwardToIoQueue nebo nesprávné zpracování selhání má za následek opuštěné žádosti WDFREQUEST.
- Udržování WDFREQUEST vně fronty v nezrušitelném stavu. Viz Správa vstupně-výstupních front, dokončování vstupně-výstupních požadavků a rušení vstupně-výstupních požadavků.
- Probíhá pokus o správu zrušení pomocí funkce Mark/UnmarkCancelable místo použití funkce IoQueues. Viz Objekty fronty rozhraní.
- Neznáte rozdíl mezi operacemi vyčištění a zavření popisovače souboru. Viz chyby při zpracování operací vyčištění a zavření.
- Přehlížení potenciálních rekurzí při dokončení vstupně-výstupních operací a jejich opětovném odeslání z dokončovací rutiny.
- Neposkytování explicitních informací o atributech řízení spotřeby WDFQUEUEs. Není jasně zdokumentována volba řízení spotřeby. Toto je hlavní příčina kontroly chyby 0x9F: DRIVER_POWER_STATE_FAILURE v ovladačích WDF. Když se zařízení odebere, framework vyprázdní vstupně-výstupní operace z fronty spravované napájením a fronty nespravované napájením v různých fázích procesu odebrání. Fronty bez správy napájení se vyprázdní, když je přijata konečná IRP_MN_REMOVE_DEVICE. Pokud tedy držíte vstupně-výstupní operace ve frontě bez správy napájení, je vhodné explicitně vyprázdnit vstupně-výstupní operace v kontextu EvtDeviceSelfManagedIoFlush, abyste se vyhnuli zablokování.
- Nedodržuje pravidla zacházení s IRP. Viz chyby při zpracování operací vyčištění a zavření.
Synchronizace
- Držení zámků pro kód, který nepotřebuje ochranu. Zámek pro celou funkci neudržujte, pokud je potřeba chránit pouze malý počet operací.
- Volání z řidičů s držením zámků. Toto jsou hlavní příčiny zablokování.
- Použití prokládaných primitiv k vytvoření schématu uzamykání místo používání vhodných, systémově poskytovaných primitiv, jako jsou mutexy, semafory a spinlocky. Viz Úvod k objektům mutex, objektům semaforů a Úvod ke spinlockům.
- Použití spinlocku, kde by byl vhodnější pasivní zámek. Viz Rychlé mutexy a strážené mutexy a objekty událostí. Další pohled na zámky najdete v článku OSR - Stav synchronizace.
- Vyjádření souhlasu se synchronizací a modelem na úrovni spouštění WDF bez plného porozumění důsledkům Viz použití zámků architektury. Pokud váš ovladač není monolitický ovladač nejvyšší úrovně, který přímo komunikuje s hardwarem, vyhněte se vyjádření souhlasu se synchronizací WDF, protože může vést k zablokování kvůli rekurzi.
- Získání KEVENT, Semaphore, ERESOURCE, UnsafeFastMutex v prostředí více vláken bez vstupu do kritické oblasti. To může vést k útoku DOS, protože vlákno držící jeden z těchto zámků může být pozastaveno. Viz Úvod k objektům dispečera jádra.
- Přidělení KEVENT v zásobníku vláken a vrácení volajícímu, zatímco událost se stále používá. Obvykle se provádí při použití s IoBuildSynchronousFsdRequest nebo IoBuildDeviceIoControlRequest. Volající těchto volání by se měl ujistit, že se neodvinou ze zásobníku, dokud správce vstupně-výstupních operací nesignalizuje událost při dokončení IRP.
- Nekonečné čekání v dispečerských rutinách. Obecně platí, že jakýkoli druh čekání v rutině odeslání je špatný postup.
- Nevhodně kontrolovat existenci objektu (pokud blah == NULL) před jeho odstraněním. Obvykle to znamená, že autor nemá úplný přehled o kódu, který řídí životnost objektu.
Správa objektů
- Není explicitně nastaveno rodičovství objektů WDF. Viz Úvod k objektům rámce.
- Nadřazení objektu WDF na WDFDRIVER místo nadřazení objektu, který poskytuje lepší správu životnosti a optimalizuje využití paměti. Například přiřazení WDFREQUEST k WDFDEVICE než k IOTARGET. Viz použití obecných objektů rámci, životní cyklus objektů rámce a souhrn objektů rámce.
- Neprovádí se ochrana prostředků sdílené paměti přístupných mezi ovladači. Viz funkci ExInitializeRundownProtection.
- Chybné zařazení stejné pracovní položky do fronty, když předchozí položka je již ve frontě nebo již běží. Toto by mohlo být problémem, pokud klient předpokládá, že se všechny pracovní položky ve frontě skutečně spustí. Viz část použití rozhraní WorkItems. Další informace o řazení do fronty WorkItems naleznete v modulu DMF_QueuedWorkitem v projektu Driver Module Framework (DMF) - https://github.com/Microsoft/DMF.
- Načasování fronty před odesláním zprávy, kterou má časovač zpracovat. Viz použití časovačů.
- Provedení operace v pracovní položce, která může blokovat nebo trvat neomezeně dlouhou dobu.
- Návrh řešení, které má za následek velké množství pracovních položek určených k zařazení do fronty. Může vést k nereagujícímu systému nebo útoku DOS, pokud špatný člověk může řídit akci (např. pumpování vstupně-výstupních operací na ovladač, který zařadí novou pracovní položku do fronty pro každou vstupně-výstupní operaci). Viz použití pracovních položek rámce.
- Nezajištění, že zpětná volání DPC pracovní položky byla dokončena před odstraněním objektu. Viz pokyny pro psaní rutin DPC a funkce WdfDpcCancel.
- Vytváření vláken místo použití pracovních položek pro krátké úlohy nebo úlohy bez dotazování Podívejte se na systémová pracovní vlákna.
- Před odinstalováním nebo uvolněním ovladače zajistěte, aby vlákna byla dokončena. Další informace o synchronizaci ukončení vlákna najdete v kódu přidruženém k modulu DMF_Thread v projektu Driver Module Framework (DMF) – https://github.com/Microsoft/DMF.
- Použití jednoho ovladače ke správě zařízení, která jsou odlišná, ale vzájemně závislá, a použití globálních proměnných ke sdílení informací.
Paměť
- Neoznačuje kód pasivního spuštění jako PAGEABLE, pokud je to možné. Kód stránkovacího ovladače může snížit velikost paměťového nároku kódu ovladače, čímž se uvolní systémové prostředky pro jiné použití. Buďte opatrní při označování stránkovatelného kódu, který zvyšuje IRQL >= DISPATCH_LEVEL, nebo který lze volat při zvýšené úrovni IRQL. Viz Kdy by měl kód a data být stránkovatelné a vytváření stránkovatelných ovladačů a zjišťování kódu, který může být stránkovatelný.
- Deklarování velkých struktur v zásobníku by mělo použít haldu/poolinstead. Viz Použití kernelového zásobníku a přidělování System-Space paměti.
- Zbytečné nulování kontextu objektu WDF. To může znamenat nedostatek přehlednosti o tom, kdy se paměť automaticky vynuluje.
Obecné pokyny pro ovladače
- Míchání primitiv WDM a WDF Použití primitiv WDM, kde lze použít primitiva WDF. Použití primitiv WDF vás chrání před neočekávanými problémy, zlepšuje ladění a co je důležitější, umožňuje, aby byl váš ovladač přenositelný do uživatelského režimu.
- Pojmenování FDO a vytváření symbolických odkazů, pokud není potřeba. Viz Správa řízení přístupu ovladačů.
- Kopírování, vkládání a používání GUID a dalších konstantních hodnot z ukázkových ovladačů.
- Zvažte použití opensourcového kódu DMF (Driver Module Framework) v projektu ovladače. DMF je rozšíření WDF, které umožňuje dodatečné funkce pro vývojáře ovladačů WDF. Viz Představujeme architekturu modulu ovladače.
- Použití registru jako mechanismu oznámení mezi procesy nebo jako poštovní schránky Alternativu najdete v DMF_NotifyUserWithEvent a DMF_NotifyUserWithRequest modulech dostupných v projektu DMF – https://github.com/Microsoft/DMF.
- Za předpokladu, že všechny části registru budou k dispozici pro přístup během počáteční fáze spouštění systému.
- Závislost na pořadí zatížení jiného ovladače nebo služby. Vzhledem k tomu, že pořadí zatížení lze změnit mimo kontrolu ovladače, může to vést k tomu, že ovladač, který funguje zpočátku, ale později selže v nepředvídatelném vzoru.
- Opětovné vytvoření knihoven ovladačů, které jsou již k dispozici, například knihovny, které WDF poskytuje pro PnP, popsané v tématu Podpora PnP a Řízení spotřeby ve vašem ovladači, nebo knihovny dostupné v rozhraní sběrnice, jak je popsáno v článku OSR Použití rozhraní sběrnice pro komunikaci ovladač-ovladač.
PnP/Power
- Komunikace s jiným ovladačem způsobem, který není přátelský vůči PnP – nepřihlašování k odběru oznámení změn zařízení PnP. Viz oznámení o registraci změny rozhraní zařízení.
- Vytváření uzlů ACPI k vytvoření výčtu zařízení a vytváření závislostí napájení mezi nimi místo použití ovladače sběrnice nebo systému poskytovaného rozhraní pro vytváření softwarových zařízení pro PNP a závislosti napájení elegantně. Vizte Podpora PnP a správa napájení v ovladačích funkcí.
- Označení zařízení jako nezakazovatelné – vynucení restartování při aktualizaci ovladače
- Skrytí zařízení ve Správci zařízení Viz Skrytí zařízení ve Správci zařízení.
- Předpokládá se, že ovladač bude použit pouze pro jednu instanci zařízení.
- Vytváření předpokladů, že se ovladač nikdy nevyloží. Podívejte se na rutinu uvolnění ovladače PnP .
- Nezpracovává se falešné oznámení o příchodu rozhraní. K tomu může dojít a očekává se, že řidiči budou tento stav bezpečně řešit.
- Neimplementování politiky nečinnosti S0 pro spotřebu energie je důležité pro zařízení, která mají omezení DRIPS nebo jsou jejich podřízenými jednotkami. Viz podpora nečinného vypnutí napájení.
- Nekontrolování WdfDeviceStopIdle stav návratu vede k úniku energetické reference kvůli nerovnováze WdfDeviceStopIdle/ResumeIdle a nakonec ke kontrole chyby 9F.
- Netušili jste, že kvůli vyrovnávání prostředků lze volat funkce PrepareHardware nebo ReleaseHardware více než jednou. Tyto zpětná volání by měla být omezena na inicializaci hardwarových prostředků. Vizte EVT_WDF_DEVICE_PREPARE_HARDWARE.
- Použití prepareHardware/ReleaseHardware k přidělování softwarových prostředků Softwareové přidělení prostředků statické pro zařízení by se mělo provést buď v AddDevice, nebo v SelfManagedIoInit, pokud přidělení prostředků vyžaduje interakci s hardwarem. Podívejte se na EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT.
Pokyny pro kódování
- Nepoužívá bezpečné řetězcové a celočíselné funkce. Viz Použití bezpečných řetězcových funkcí a použití bezpečných celočíselných funkcí.
- K definování konstant nepoužívejte typedefs.
- Použití globálních a statických proměnných Vyhněte se ukládání kontextu jednotlivých zařízení v globálních prostředích. Globální organizace jsou určené ke sdílení informací mezi několika instancemi zařízení. Jako alternativu zvažte použití kontextu objektu WDFDRIVER ke sdílení informací napříč několika instancemi zařízení.
- Nepoužívejte popisné názvy proměnných.
- Nekonzistentní pojmenovávání proměnných – konzistence malých a velkých písmen. Při aktualizaci existujícího kódu nenásleduje existující styl kódování. Například použití různých názvů proměnných pro běžné struktury v různých funkcích.
- Nekomentování důležitých možností návrhu – řízení spotřeby, zámky, správa stavu, používání pracovních položek, DPC, časovače, globální využití prostředků, předběžné přidělení prostředků, komplexní výrazy/podmíněné příkazy.
- Komentování věcí, které jsou zřejmé z názvu volaného rozhraní API. Vytvoření komentáře ekvivalentem názvu funkce v anglickém jazyce (například zápis komentáře "Create the Device Object" při volání WdfDeviceCreate).
- Nevytvávejte makra, která mají návratové volání. Viz funkce (C++).
- Žádné nebo neúplné poznámky ke zdrojovému kódu (SAL). Viz SAL 2.0 Poznámky pro ovladače systému Windows.
- Použití maker místo inline funkcí
- Použití maker pro konstanty místo constexpr při použití jazyka C++
- Kompilace ovladače pomocí kompilátoru jazyka C namísto kompilátoru jazyka C++ pro zajištění silné kontroly typů.
Zpracování chyb
- Nehlásí kritické chyby ovladačů a řádně označí zařízení jako nefunkční.
- Nevrací odpovídající stav chyby NT, který se překládá na smysluplný stav chyby WIN32. Viz použití hodnot NTSTATUS.
- Nepoužíváte makra NTSTATUS ke kontrole vráceného stavu systémových funkcí.
- Neprovádí se kontrola stavových proměnných nebo příznaků tam, kde je to potřeba.
- Zkontrolujte, jestli je ukazatel platný, než k němu přistupujete, abyste mohli obejít závodní podmínky.
- Kontrola na NULL ukazatele. Pokud se pokusíte použít ukazatel NULL pro přístup k paměti, Windows provede kontrolu chyby (bug check). Parametry kontroly chyb poskytnou potřebné informace k opravě ukazatele null. Přesčas, když se do kódu přidá mnoho nepotřebných příkazů ASSERT, spotřebovávají paměť a zpomalují systém.
- ASSERTING na ukazateli kontextu objektu. Architektura ovladačů zaručuje, že objekt bude vždy přidělen s kontextem.
Trasování
- Nedefinování vlastních typů WPP a použití ve voláních trasování pro získání snadno čitelných trasovacích zpráv. Viz Přidání softwarového trasování WPP do ovladače systému Windows.
- Nepoužívá trasování IFR. Viz použití záznamníku trasování za letu (IFR) v ovladačích KMDF a UMDF 2.
- Vyvolání názvů funkcí ve trasovacích voláních WPP. WPP už sleduje názvy funkcí a čísla řádků.
- Nepoužívání událostí trasování událostí pro Windows k měření výkonu a dalších událostí ovlivňujících kritické aspekty uživatelské zkušenosti. Podívejte se na Přidání trasování událostí do ovladačů Kernel-Mode.
- Nehlásí kritické chyby v protokolu událostí a řádně označí zařízení jako nefunkční.
Ověření
- Během vývoje a testování neběží ověřovatel ovladačů se standardním i pokročilým nastavením. Viz Driver Verifier. V upřesňujících nastaveních se doporučuje povolit všechna pravidla s výjimkou pravidel souvisejících s nízkou simulací prostředků. Je vhodné spustit testy simulace nízkých prostředků izolovaně, aby bylo snazší ladit problémy.
- Test DevFund by se neměl spouštět na ovladači nebo na třídě zařízení, jejíž je ovladač součástí, pokud je povoleno pokročilé nastavení ověřovatele. Viz Spuštění testů DevFund prostřednictvím příkazového řádku.
- Neověřuje se, zda je ovladač kompatibilní s HVCI. Viz Implementujte kód kompatibilní s HVCI.
- Během vývoje a testování ovladačů uživatelského režimu neběží na WUDFhost.exe AppVerifier. Viz ověřovač aplikací.
- Neprovádí kontrolu využití paměti pomocí rozšíření ladicího programu !wdfpoolusage během běhu aplikace, aby bylo zajištěno, že objekty WDF nejsou ponechány bez dozoru. Běžnou obětí těchto problémů jsou paměť, požadavky a pracovní položky.
- Nezapomeňte použít rozšíření ladicího programu !wdfkd ke kontrole stromu objektů, aby se zajistilo, že objekty jsou správně nadřazené, a zkontrolovat atributy hlavních objektů, jako jsou WDFDRIVER, WDFDEVICE, IO.