Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Listas Ligadas Simples
O sistema operacional fornece suporte interno para listas vinculadas individualmente que usam estruturas SINGLE_LIST_ENTRY . Uma lista vinculada individualmente consiste em um cabeçalho de lista mais algum número de entradas de lista. (O número de entradas da lista é zero se a lista estiver vazia.) Cada entrada de lista é representada como uma estrutura SINGLE_LIST_ENTRY . O cabeçalho da lista também é representado como uma estrutura SINGLE_LIST_ENTRY .
Cada estrutura SINGLE_LIST_ENTRY contém um membro Next que aponta para outra estrutura SINGLE_LIST_ENTRY . Na estrutura SINGLE_LIST_ENTRY que representa o cabeçalho da lista, o membro Next aponta para a primeira entrada na lista ou é NULL se a lista estiver vazia. Na estrutura SINGLE_LIST_ENTRY que representa uma entrada na lista, o membro Próximo aponta para a próxima entrada da lista ou é NULL se essa entrada for a última da lista.
As rotinas que manipulam uma lista vinculada individualmente levam um ponteiro para um SINGLE_LIST_ENTRY que representa o cabeçalho da lista. Eles atualizam o ponteiro Next para que ele aponte para a primeira entrada da lista após a operação.
Suponha que a variável ListHead seja um ponteiro para a estrutura SINGLE_LIST_ENTRY que representa o cabeçalho da lista. Um driver manipula ListHead da seguinte maneira:
Para inicializar a lista como vazia, defina ListHead-Next> como NULL.
Para adicionar uma nova entrada à lista, aloque um SINGLE_LIST_ENTRY para representar a nova entrada e chame PushEntryList para adicionar a entrada ao início da lista.
Remova a primeira entrada da lista usando PopEntryList.
Um SINGLE_LIST_ENTRY, por si só, só tem um membro Next . Para armazenar seus próprios dados nas listas, incorpore o SINGLE_LIST_ENTRY como um membro da estrutura que descreve a entrada da lista, da seguinte maneira.
typedef struct {
// driver-defined members
.
.
.
SINGLE_LIST_ENTRY SingleListEntry;
// other driver-defined members
.
.
.
} XXX_ENTRY;
Para adicionar uma nova entrada à lista, aloque uma estrutura XXX_ENTRY e, em seguida, passe um ponteiro para o membro SingleListEntry para PushEntryList. Para converter um ponteiro para o SINGLE_LIST_ENTRY de volta para um XXX_ENTRY, use CONTAINING_RECORD. Aqui está um exemplo de rotinas que inserem e removem entradas definidas pelo driver de uma lista vinculada individualmente.
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);
}
O sistema também fornece versões atómicas das operações de lista, ExInterlockedPopEntryList e ExInterlockedPushEntryList. Cada um usa um parâmetro de bloqueio de rotação extra. A rotina adquire o bloqueio de rotação antes de atualizar a lista e, em seguida, a rotina libera o bloqueio de rotação após a conclusão da operação. Enquanto o bloqueio é mantido, as interrupções são desativadas. Cada operação na lista deve usar o mesmo bloqueio de rotação para garantir que cada uma dessas operações na lista seja sincronizada com todas as outras operações. Você deve usar o spin lock somente com essas rotinas ExInterlockedXxxList. Não use o bloqueio de rotação para qualquer outra finalidade. Os drivers podem usar o mesmo bloqueio para várias listas, mas esse comportamento aumenta a contenção de bloqueio, por isso os drivers devem evitá-lo.
Por exemplo, as rotinas ExInterlockedPopEntryList e ExInterlockedPushEntryList podem oferecer suporte ao compartilhamento de uma lista vinculada individualmente por um thread de driver em execução em IRQL = PASSIVE_LEVEL e um ISR em execução em DIRQL. Essas rotinas desativam interrupções quando o bloqueio de rotação é mantido. Assim, o ISR e o thread do driver podem usar com segurança o mesmo bloqueio de rotação em suas chamadas para essas rotinas ExInterlockedXxxList sem arriscar um impasse.
Não misture chamadas para as versões atómica e não atómica das operações de lista na mesma lista. Se as versões atômica e não atômica forem executadas simultaneamente na mesma lista, a estrutura de dados pode ficar corrompida e o computador pode parar de responder ou verificar bugs (ou seja, falhar). (Você não pode adquirir o bloqueio de rotação ao chamar a rotina não atômica como uma alternativa à mistura de chamadas para versões atômicas e não atômicas de operações de lista. O uso do bloqueio de rotação dessa maneira não é suportado e ainda pode causar corrupção na lista.)
O sistema também fornece uma implementação alternativa de listas atómicas ligadas individualmente que é mais eficiente. Para obter mais informações, consulte Listas vinculadas individualmente sequenciadas.
Listas duplamente vinculadas
O sistema operacional fornece suporte interno para listas duplamente vinculadas que usam estruturas LIST_ENTRY . Uma lista duplamente vinculada consiste em um cabeçalho de lista mais algum número de entradas de lista. (O número de entradas da lista é zero se a lista estiver vazia.) Cada entrada de lista é representada como uma estrutura LIST_ENTRY . O cabeçalho da lista também é representado como uma estrutura LIST_ENTRY .
Cada estrutura LIST_ENTRY contém um membro Flink e um membro Blink . Ambos os membros são apontadores para estruturas LIST_ENTRY.
Na estrutura LIST_ENTRY que representa o cabeçalho da lista, o membro Flink aponta para a primeira entrada na lista e o membro Blink aponta para a última entrada na lista. Se a lista estiver vazia, Flink e Blink do cabeçalho da lista apontam para o próprio cabeçalho da lista.
Na estrutura LIST_ENTRY que representa uma entrada na lista, o membro Flink aponta para a próxima entrada na lista e o membro Blink aponta para a entrada anterior na lista. (Se a entrada for a última da lista, Flink aponta para o cabeçalho da lista. Da mesma forma, se a entrada for a primeira da lista, Blink aponta para o cabeçalho da lista.)
Embora essas regras possam parecer surpreendentes à primeira vista, elas permitem que as operações de inserção e remoção de lista sejam implementadas sem ramificações de código condicionais.
As rotinas que manipulam uma lista duplamente vinculada levam um ponteiro para um LIST_ENTRY que representa o cabeçalho da lista. Essas rotinas atualizam os membros Flink e Blink no cabeçalho da lista para que esses membros apontem para a primeira e a última entradas na lista resultante.
Suponha que a variável ListHead seja um ponteiro para a estrutura LIST_ENTRY que representa o cabeçalho da lista. Um driver manipula ListHead da seguinte maneira:
Para inicializar a lista como vazia, use InitializeListHead, que inicializa ListHead->Flink e ListHead->Blink para indicar a ListHead.
Para inserir uma nova entrada no topo da lista, atribua um LIST_ENTRY para representar a nova entrada e, em seguida, chame InsertHeadList para inserir a entrada no início da lista.
Para acrescentar uma nova entrada à parte final da lista, atribua um LIST_ENTRY para representar a nova entrada e, em seguida, chame InsertTailList para inserir a entrada no final da lista.
Para remover a primeira entrada da lista, use RemoveHeadList. Isso retorna um ponteiro para a entrada removida da lista ou para ListHead se a lista estiver vazia.
Para remover a última entrada da lista, use RemoveTailList. Isso retorna um ponteiro para a entrada removida da lista ou para ListHead se a lista estiver vazia.
Para remover uma entrada especificada da lista, use RemoveEntryList.
Para verificar se uma lista está vazia, use IsListEmpty.
Para acrescentar uma lista ao final de outra lista, use AppendTailList.
Um LIST_ENTRY, por si só, só tem membros Blink e Flink . Para armazenar seus próprios dados nas listas, incorpore o LIST_ENTRY como um membro da estrutura que descreve a entrada da lista, da seguinte maneira.
typedef struct {
// driver-defined members
.
.
.
LIST_ENTRY ListEntry;
// other driver-defined members.
.
.
.
} XXX_ENTRY;
Para adicionar uma nova entrada a uma lista, aloque uma estrutura XXX_ENTRY e, em seguida, passe um ponteiro para o membro ListEntry para InsertHeadList ou InsertTailList. Para converter um ponteiro em um LIST_ENTRY de volta para um XXX_ENTRY, use CONTAINING_RECORD. Para obter um exemplo dessa técnica, usando listas vinculadas individualmente, consulte Listas vinculadas individualmente acima.
O sistema também fornece versões atômicas das operações de lista, ExInterlockedInsertHeadList, ExInterlockedInsertTailList e ExInterlockedRemoveHeadList. Não há nenhuma versão atômica de RemoveTailList ou RemoveEntryList. Cada rotina usa um parâmetro extra de bloqueio de rotação. A rotina adquire o bloqueio de rotação antes de atualizar a lista e, em seguida, libera o bloqueio de rotação após a conclusão da operação. Enquanto o bloqueio é mantido, as interrupções são desativadas. Cada operação na lista deve usar o mesmo bloqueio de rotação para garantir que cada uma dessas operações na lista seja sincronizada com todas as outras. Você deve usar o spin lock somente com essas rotinas ExInterlockedXxxList. Não use o bloqueio de rotação para qualquer outra finalidade. Os drivers podem usar o mesmo bloqueio para várias listas, mas esse comportamento aumenta a contenção de bloqueio, por isso os drivers devem evitá-lo.
Por exemplo, as rotinas ExInterlockedInsertHeadList, ExInterlockedInsertTailList e ExInterlockedRemoveHeadList podem oferecer suporte ao compartilhamento de uma lista duplamente vinculada por um thread de driver em execução em IRQL = PASSIVE_LEVEL e um ISR em execução em DIRQL. Essas rotinas desativam interrupções quando o bloqueio de rotação é mantido. Assim, o ISR e o thread do driver podem usar com segurança o mesmo bloqueio de rotação em suas chamadas para essas rotinas ExInterlockedXxxList sem arriscar um impasse.
Não misture chamadas para as versões atómica e não atómica das operações de lista na mesma lista. Se as versões atômica e não atômica forem executadas simultaneamente na mesma lista, a estrutura de dados pode ficar corrompida e o computador pode parar de responder ou verificar bugs (ou seja, falhar). (Você não pode adquirir o bloqueio de rotação ao chamar a rotina não atômica para evitar misturar chamadas para versões atômicas e não atômicas das operações de lista. O uso do bloqueio de rotação dessa maneira não é suportado e ainda pode causar corrupção na lista.)
Listas Ligadas Simples Sequenciadas
Uma lista sequenciada ligada individualmente é uma implementação de listas ligadas individualmente que suporta operações atómicas. É mais eficiente para operações atômicas do que a implementação de listas vinculadas individualmente descritas em Listas vinculadas individualmente.
Uma estrutura SLIST_HEADER é usada para descrever o cabeçalho de uma lista sequenciada individualmente vinculada, enquanto SLIST_ENTRY é usada para descrever uma entrada na lista.
Um driver manipula a lista da seguinte maneira:
Para inicializar uma estrutura SLIST_HEADER , use ExInitializeSListHead.
Para adicionar uma nova entrada à lista, aloque um SLIST_ENTRY para representar a nova entrada e chame ExInterlockedPushEntrySList para adicionar a entrada ao início da lista.
Remova a primeira entrada da lista usando ExInterlockedPopEntrySList.
Para limpar completamente a lista, use ExInterlockedFlushSList.
Para determinar o número de entradas na lista, use ExQueryDepthSList.
Uma estrutura SLIST_ENTRY, por si só, apenas possui um membro Next. Para armazenar seus próprios dados nas listas, incorpore o SLIST_ENTRY como um membro da estrutura que descreve a entrada da lista, da seguinte maneira.
typedef struct
{
// driver-defined members
.
.
.
SLIST_ENTRY SListEntry;
// other driver-defined members
.
.
.
} XXX_ENTRY;
Para adicionar uma nova entrada à lista, aloque uma estrutura XXX_ENTRY e, em seguida, passe um ponteiro para o membro SListEntry para ExInterlockedPushEntrySList.
Para converter um ponteiro para o SLIST_ENTRY de volta para um XXX_ENTRY, use CONTAINING_RECORD. Para obter um exemplo dessa técnica, usando listas vinculadas individualmente não sequenciadas, consulte Listas vinculadas individualmente.
Advertência Para sistemas operacionais Microsoft Windows de 64 bits, SLIST_ENTRY estruturas devem estar alinhadas a 16 bytes.