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.
Jednosměrně propojené seznamy
Operační systém poskytuje integrovanou podporu pro jednoduše propojené seznamy, které používají struktury SINGLE_LIST_ENTRY. Jednosměrně propojený seznam se skládá z hlavy seznamu a určitého počtu položek seznamu. (Počet položek seznamu je nula, pokud je seznam prázdný.) Každá položka seznamu je reprezentována jako struktura SINGLE_LIST_ENTRY . Hlava seznamu je také reprezentována jako SINGLE_LIST_ENTRY struktura.
Každá SINGLE_LIST_ENTRY struktura obsahuje další člen , který odkazuje na jinou SINGLE_LIST_ENTRY strukturu. Ve struktuře SINGLE_LIST_ENTRY , která představuje záhlaví seznamu, další člen odkazuje na první položku v seznamu nebo má hodnotu NULL, pokud je seznam prázdný. Ve struktuře SINGLE_LIST_ENTRY , která představuje položku v seznamu, další člen odkazuje na další položku seznamu nebo má hodnotu NULL, pokud je tato položka poslední v seznamu.
Rutiny, které manipulují s jednosměrným propojeným seznamem, převezmou ukazatel na SINGLE_LIST_ENTRY, který představuje záhlaví seznamu. Aktualizují ukazatel Další tak, aby odkazovali na první položku seznamu po operaci.
Předpokládejme, že proměnná ListHead je ukazatel na strukturu SINGLE_LIST_ENTRY , která představuje záhlaví seznamu. Ovladač manipuluje s listHead následujícím způsobem:
Pokud chcete seznam inicializovat jako prázdný, nastavte ListHead-Next> na hodnotu NULL.
Chcete-li přidat novou položku do seznamu, přidělte SINGLE_LIST_ENTRY, aby představovala novou položku, a potom zavolejte funkci PushEntryList, abyste položku přidali na začátek seznamu.
Vyskočte první položku ze seznamu pomocí PopEntryList.
SINGLE_LIST_ENTRY má sám o sobě pouze Next člena. Pokud chcete do seznamů uložit vlastní data, vložte SINGLE_LIST_ENTRY jako člen struktury, která popisuje položku seznamu, následujícím způsobem.
typedef struct {
// driver-defined members
.
.
.
SINGLE_LIST_ENTRY SingleListEntry;
// other driver-defined members
.
.
.
} XXX_ENTRY;
Pokud chcete do seznamu přidat novou položku, přidělte strukturu XXX_ENTRY a pak předejte ukazatel na člena SingleListEntryPushEntryList. Pokud chcete převést ukazatel na SINGLE_LIST_ENTRY zpět na XXX_ENTRY, použijte CONTAINING_RECORD. Zde je příklad rutin, které vkládají a odebírají položky definované ovladačem z jednoduše propojeného seznamu.
typedef struct {
PVOID DriverData1;
SINGLE_LIST_ENTRY SingleListEntry;
ULONG DriverData2;
} XXX_ENTRY, *PXXX_ENTRY;
void
PushXxxEntry(PSINGLE_LIST_ENTRY ListHead, PXXX_ENTRY Entry)
{
PushEntryList(ListHead, &(Entry->SingleListEntry));
}
PXXX_ENTRY
PopXxxEntry(PSINGLE_LIST_ENTRY ListHead)
{
PSINGLE_LIST_ENTRY SingleListEntry;
SingleListEntry = PopEntryList(ListHead);
return CONTAINING_RECORD(SingleListEntry, XXX_ENTRY, SingleListEntry);
}
Systém také poskytuje atomické verze operací seznamu, ExInterlockedPopEntryList a ExInterlockedPushEntryList. Každý z nich má další parametr spinlock. Rutina získá spinlock před aktualizací seznamu a uvolní ho po dokončení operace. Při držení zámku jsou přerušení zakázána. Každá operace v seznamu musí používat stejný spinlock, aby byla každá taková operace v seznamu synchronizovaná s každou jinou operací. Zámek otáčení je nutné použít pouze s těmito rutinami ExInterlockedXxxList . Spinlock nepoužívejte k žádnému jinému účelu. Ovladače můžou použít stejný zámek pro více seznamů, ale toto chování zvyšuje soutěžení o zámky, takže by se mu měli ovladače vyhnout.
Například rutiny ExInterlockedPopEntryList a ExInterlockedPushEntryList mohou podporovat sdílení singly propojeného seznamu pomocí vlákna ovladače spuštěného v IRQL = PASSIVE_LEVEL a ISR běžícího v DIRQL. Tyto rutiny vypnou přerušení, když je držen spinlock. Proto mohou vlákno ISR a vlákno ovladače bezpečně použít stejný spinový zámek při volání těchto rutin ExInterlockedXxxList bez rizika stavu zablokování.
Nekombinujte volání atomických a neatomických verzí operací seznamu ve stejném seznamu. Pokud se atomické a neatomické verze spouští současně ve stejném seznamu, může dojít k poškození datové struktury a počítač může přestat reagovat nebo kontrolovat chyby (to znamená chybové ukončení). (Spinlock nelze získat při volání neatomární rutiny jako alternativu ke kombinování volání atomárních a neatomárních verzí operací seznamu. Použití spinlocku tímto způsobem není podporováno a může způsobit poškození seznamu.)
Systém také poskytuje alternativní implementaci atomových jednokřídlých seznamů, který je efektivnější. Další informace najdete v tématu Uspořádané seznamy s jednosměrně propojenými seznamy.
Obousměrně propojené seznamy
Operační systém poskytuje integrovanou podporu pro dvojitě propojené seznamy, které používají struktury LIST_ENTRY. Doubly propojený seznam se skládá z hlavy seznamu a určitého počtu položek seznamu. (Počet položek seznamu je nula, pokud je seznam prázdný.) Každá položka seznamu je reprezentována jako struktura LIST_ENTRY . Hlava seznamu je také reprezentována jako LIST_ENTRY struktura.
Každá LIST_ENTRY struktura obsahuje člen Flink a Blink člen. Oba členy jsou ukazatele na struktury LIST_ENTRY.
V LIST_ENTRY struktuře , která představuje záhlaví seznamu, Flink člen odkazuje na první položku v seznamu a Blink člen odkazuje na poslední položku v seznamu. Pokud je seznam prázdný, Flink a Blink hlavičky seznamu ukazují na samotnou hlavičku seznamu.
V LIST_ENTRY struktuře , která představuje položku v seznamu, Flink člen odkazuje na další položku v seznamu a Blink člen odkazuje na předchozí položku v seznamu. (Pokud je položka poslední položkou v seznamu, Flink odkazuje na záhlaví seznamu. Podobně platí, že pokud je položka první položkou v seznamu, bliká na záhlaví seznamu.)
I když se tato pravidla na první pohled můžou zdát překvapivá, umožňují implementaci operací vložení a odebrání seznamu bez větví podmíněného kódu.
Rutiny, které manipulují s propojeným seznamem, převezmou ukazatel na LIST_ENTRY , který představuje záhlaví seznamu. Tyto rutiny aktualizují členy Flink a Blink v záhlaví seznamu tak, aby tyto členy ukazovaly na první a poslední položky ve výsledném seznamu.
Předpokládejme, že proměnná ListHead je ukazatel na strukturu LIST_ENTRY , která představuje záhlaví seznamu. Ovladač manipuluje s listHead následujícím způsobem:
Pokud chcete seznam inicializovat jako prázdný, použijte InitializeListHead, která inicializuje ListHead->Flink a ListHead->Blink, aby ukazovaly na ListHead.
Pokud chcete vložit novou položku do hlavy seznamu, přidělte LIST_ENTRY , která bude představovat novou položku, a potom zavoláte InsertHeadList pro vložení položky na začátek seznamu.
Chcete-li připojit novou položku na konec seznamu, alokujte LIST_ENTRY představující novou položku a poté zavolejte funkci InsertTailList, která vloží položku na konec seznamu.
Pokud chcete odebrat první položku ze seznamu, použijte RemoveHeadList. Vrátí ukazatel na odebranou položku ze seznamu nebo na ListHead , pokud je seznam prázdný.
Pokud chcete odebrat poslední položku ze seznamu, použijte RemoveTailList. Vrátí ukazatel na odebranou položku ze seznamu nebo na ListHead , pokud je seznam prázdný.
Pokud chcete ze seznamu odebrat zadanou položku, použijte RemoveEntryList.
Pokud chcete zjistit, jestli je seznam prázdný, použijte IsListEmpty.
Pokud chcete přidat seznam na konec jiného seznamu, použijte AppendTailList.
LIST_ENTRY má pouze Blink a Flink členy. Pokud chcete do seznamů uložit vlastní data, vložte LIST_ENTRY jako člen struktury, která popisuje položku seznamu, následujícím způsobem.
typedef struct {
// driver-defined members
.
.
.
LIST_ENTRY ListEntry;
// other driver-defined members.
.
.
.
} XXX_ENTRY;
Pokud chcete do seznamu přidat novou položku, přidělte strukturu XXX_ENTRY a pak předejte ukazatel na člena ListEntryInsertHeadList nebo InsertTailList. Pokud chcete převést ukazatel na LIST_ENTRY zpět na XXX_ENTRY, použijte CONTAINING_RECORD. Příklad této techniky, použití jednosměrně propojených seznamů, viz výše Jednosměrně Propojené Seznamy.
Systém také poskytuje atomické verze operací seznamu, ExInterlockedInsertHeadList, ExInterlockedInsertTailList a ExInterlockedRemoveHeadList. Neexistuje žádná atomická verze RemoveTailList ani RemoveEntryList. Každá rutina přebírá další parametr spinlock. Rutina získá spinlock před aktualizací seznamu a potom uvolní spinlock po dokončení. Při držení zámku jsou přerušení zakázána. Každá operace v seznamu musí používat stejný spinlock, aby byla každá taková operace v seznamu synchronizována s ostatními. Zámek otáčení je nutné použít pouze s těmito rutinami ExInterlockedXxxList . Spinlock nepoužívejte k žádnému jinému účelu. Ovladače můžou použít stejný zámek pro více seznamů, ale toto chování zvyšuje soutěžení o zámky, takže by se mu měli ovladače vyhnout.
Například rutiny ExInterlockedInsertHeadList, ExInterlockedInsertTailList a ExInterlockedRemoveHeadList můžou podporovat sdílení doubly propojeného seznamu pomocí vlákna ovladače spuštěného v IRQL = PASSIVE_LEVEL a ISR běžící na DIRQL. Tyto rutiny vypnou přerušení, když je držen spinlock. Proto mohou vlákno ISR a vlákno ovladače bezpečně použít stejný spinový zámek při volání těchto rutin ExInterlockedXxxList bez rizika stavu zablokování.
Nekombinujte volání atomických a neatomických verzí operací seznamu ve stejném seznamu. Pokud se atomické a neatomické verze spouštějí současně ve stejném seznamu, může dojít k poškození datové struktury a počítač může přestat reagovat nebo se zhroutit (tj. havárie). (Zámek spin nelze získat při volání neatomové rutiny, abyste se vyhnuli kombinování volání atomových a neatomových verzí operací seznamu. Použití spin zámku tímto způsobem není podporováno a může způsobit poškození seznamu.)
Sekvencované seznamy s jednosměrně propojenými seznamy
Sekvencovaný singly propojený seznam je implementace singly propojených seznamů, která podporuje atomické operace. Je efektivnější pro atomické operace než implementace jednoduše propojených seznamů popsaných v Jednoduše propojené seznamy.
Struktura SLIST_HEADER slouží k popisu hlavy sekvenčně propojeného seznamu, zatímco SLIST_ENTRY slouží k popisu položky v seznamu.
Ovladač pracuje se seznamem následujícím způsobem:
K inicializaci struktury SLIST_HEADER použijte ExInitializeSListHead.
Chcete-li přidat novou položku do seznamu, nejprve přidělte SLIST_ENTRY, která představuje novou položku, a poté zavolejte ExInterlockedPushEntrySList, abyste položku přidali na začátek seznamu.
Odeberte první položku ze seznamu pomocí ExInterlockedPopEntrySList.
Pokud chcete seznam úplně vymazat, použijte ExInterlockedFlushSList.
Chcete-li určit počet položek v seznamu, použijte ExQueryDepthSList.
SLIST_ENTRY má sám o sobě pouze člena Next. Pokud chcete do seznamů uložit vlastní data, vložte SLIST_ENTRY jako člen struktury, která popisuje položku seznamu, následujícím způsobem.
typedef struct
{
// driver-defined members
.
.
.
SLIST_ENTRY SListEntry;
// other driver-defined members
.
.
.
} XXX_ENTRY;
Pokud chcete do seznamu přidat novou položku, přidělte XXX_ENTRY strukturu a pak předejte ukazatel na SListEntry členu ExInterlockedPushEntrySList.
Pokud chcete převést ukazatel na SLIST_ENTRY zpět na XXX_ENTRY, použijte CONTAINING_RECORD. Příklad této techniky, využití nesekvenčních jednosměrně propojených seznamů, viz Jednosměrné propojené seznamy.
Varování V 64bitových operačních systémech Microsoft Windows musí být struktury SLIST_ENTRY zarovnané na 16 bajtů.