Bloqueando código ou dados pagináveis
Determinados drivers de modo kernel, como os drivers seriais e paralelos, não precisam ser residentes em memória, a menos que os dispositivos gerenciados estejam abertos. No entanto, desde que haja uma conexão ou porta ativa, alguma parte do código do driver que gerencia essa porta deve ser residente para atender ao dispositivo. Quando a porta ou a conexão não está sendo usada, o código do driver não é necessário. Por outro lado, um driver para um disco que contém o código do sistema, o código do aplicativo ou o arquivo de paginação do sistema deve sempre ser residente na memória porque o driver transfere constantemente os dados entre o dispositivo e o sistema.
Um driver para um dispositivo usado esporadicamente (como um modem) pode liberar espaço no sistema quando o dispositivo gerenciado não estiver ativo. Se você colocar em uma única seção o código que deve ser residente para atender a um dispositivo ativo e se o driver bloquear o código na memória enquanto o dispositivo estiver sendo usado, esta seção poderá ser designada como paginável. Quando o dispositivo do driver é aberto, o sistema operacional coloca a seção paginável na memória e o driver o bloqueia até que não seja mais necessário.
O código do driver de áudio cd do sistema usa essa técnica. O código do driver é agrupado em seções pagináveis de acordo com o fabricante do dispositivo de CD. Certas marcas podem nunca estar presentes em um determinado sistema. Além disso, mesmo que exista um CD-ROM em um sistema, ele pode ser acessado com pouca frequência, portanto, agrupar código em seções pagináveis por tipo de CD garante que o código para dispositivos que não existem em um computador específico nunca será carregado. No entanto, quando o dispositivo é acessado, o sistema carrega o código para o dispositivo de CD apropriado. Em seguida, o driver chama a rotina MmLockPagableCodeSection , conforme descrito abaixo, para bloquear seu código na memória enquanto seu dispositivo está sendo usado.
Para isolar o código paginável em uma seção nomeada, marque-o com a seguinte diretiva do compilador:
#pragma alloc_text(PAGE*Xxx, *RoutineName)
O nome de uma seção de código paginável deve começar com as quatro letras "PAGE" e pode ser seguido por até quatro caracteres (representados aqui como Xxx) para identificar exclusivamente a seção. As quatro primeiras letras do nome da seção (ou seja, "PAGE") devem ser maiúsculas. O RoutineName identifica um ponto de entrada a ser incluído na seção paginável.
O nome válido mais curto para uma seção de código paginável em um arquivo de driver é simplesmente PAGE. Por exemplo, a diretiva pragma no exemplo de código a seguir identifica RdrCreateConnection
como um ponto de entrada em uma seção de código paginável chamada PAGE.
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RdrCreateConnection)
#endif
Para tornar o código de driver paginável residente e bloqueado na memória, um driver chama MmLockPagableCodeSection, passando um endereço (normalmente o ponto de entrada de uma rotina de driver) que está na seção de código paginável. MmLockPagableCodeSection bloqueia todo o conteúdo da seção que contém a rotina referenciada na chamada. Em outras palavras, essa chamada torna todas as rotinas associadas ao mesmo identificador PAGEXxx residentes e bloqueadas na memória.
MmLockPagableCodeSection retorna um identificador a ser usado ao desbloquear a seção (chamando a rotina MmUnlockPagableImageSection ) ou quando o driver deve bloquear a seção de locais adicionais em seu código.
Um driver também pode tratar dados raramente usados como pagináveis para que eles também possam ser paginado até que o dispositivo compatível esteja ativo. Por exemplo, o driver do mixer do sistema usa dados pagináveis. O dispositivo de mixer não tem E/S assíncrona associada a ele, portanto, esse driver pode tornar seus dados pagináveis.
O nome de uma seção de dados paginável deve começar com as quatro letras "PAGE" e pode ser seguido por até quatro caracteres para identificar exclusivamente a seção. As quatro primeiras letras do nome da seção (ou seja, "PAGE") devem ser maiúsculas.
Evite atribuir nomes idênticos a seções de código e dados. Para tornar o código-fonte mais legível, os desenvolvedores de driver normalmente atribuem o nome PAGE à seção de código paginável porque esse nome é curto e pode aparecer em vários alloc_text diretivas pragma. Nomes mais longos são atribuídos a todas as seções de dados pagináveis (por exemplo, PAGEDATA para data_seg, PAGEBSS para bss_seg e assim por diante) que o driver pode exigir.
Por exemplo, as duas primeiras diretivas pragma no exemplo de código a seguir definem duas seções de dados pagináveis, PAGEDATA e PAGEBSS. PAGEDATA é declarado usando a diretiva pragma data_seg e contém dados inicializados. PAGEBSS é declarado usando a diretiva pragma bss_seg e contém dados não inicializados.
#pragma data_seg("PAGEDATA")
#pragma bss_seg("PAGEBSS")
INT Variable1 = 1;
INT Variable2;
CHAR Array1[64*1024] = { 0 };
CHAR Array2[64*1024];
#pragma data_seg()
#pragma bss_seg()
Neste exemplo de código, Variable1
e Array1
são inicializados explicitamente e, portanto, são colocados na seção PAGEDATA.
Variable2
e Array2
são implicitamente inicializados por zero e são colocados na seção PAGEBSS.
A inicialização implícita de variáveis globais para zero reduz o tamanho do arquivo executável em disco e é preferencial em vez de inicialização explícita para zero. A inicialização zero explícita deve ser evitada, exceto nos casos em que ela é necessária para colocar uma variável em uma seção de dados específica.
Para tornar uma seção de dados residente em memória e bloqueá-la na memória, um driver chama MmLockPagableDataSection, passando um item de dados que aparece na seção de dados paginável. MmLockPagableDataSection retorna um identificador a ser usado em solicitações de bloqueio ou desbloqueio subsequentes.
Para restaurar o status paginável de uma seção bloqueada, chame MmUnlockPagableImageSection, passando o valor do identificador retornado por MmLockPagableCodeSection ou MmLockPagableDataSection, conforme apropriado. A rotina Unload de um driver deve chamar MmUnlockPagableImageSection para liberar cada identificador obtido para seções de código e dados bloqueáveis.
Bloquear uma seção é uma operação cara porque o gerenciador de memória deve pesquisar sua lista de módulos carregados antes de bloquear as páginas na memória. Se um driver bloquear uma seção de muitos locais em seu código, ele deverá usar o MmLockPagableSectionByHandle mais eficiente após sua chamada inicial para a SeçãoXxxMmLockPagable.
O identificador passado para MmLockPagableSectionByHandle é o identificador retornado pela chamada anterior para MmLockPagableCodeSection ou MmLockPagableDataSection.
O gerenciador de memória mantém uma contagem para cada identificador de seção e incrementa essa contagem sempre que um driver chama MmLockPagableXxx para essa seção. Uma chamada para MmUnlockPagableImageSection diminui a contagem. Embora o contador de qualquer identificador de seção seja diferente de zero, essa seção permanece bloqueada na memória.
O identificador para uma seção é válido desde que seu driver seja carregado. Portanto, um driver deve chamar MmLockPagableXxxSection apenas uma vez. Se o driver exigir chamadas de bloqueio adicionais, ele deverá usar MmLockPagableSectionByHandle.
Se um driver chamar qualquer rotina MmLockPagable Xxx para uma seção que já está bloqueada, o gerenciador de memória incrementará a contagem de referência para a seção. Se a seção for paginada quando a rotina de bloqueio for chamada, as páginas do gerenciador de memória na seção e definirão sua contagem de referência como uma.
O uso dessa técnica minimiza o efeito do driver sobre os recursos do sistema. Quando o driver é executado, ele pode bloquear na memória o código e os dados que devem ser residentes. Quando não há solicitações de E/S pendentes para seu dispositivo (ou seja, quando o dispositivo é fechado ou se o dispositivo nunca foi aberto), o driver pode desbloquear o mesmo código ou dados, tornando-o disponível para ser paginado.
No entanto, depois que um driver tiver conectado interrupções, qualquer código de driver que possa ser chamado durante o processamento de interrupção sempre deverá ser residente na memória. Embora alguns drivers de dispositivo possam ser tornados pagináveis ou bloqueados na memória sob demanda, alguns conjuntos principais de código e dados desse driver devem ser permanentemente residentes no espaço do sistema.
Considere as diretrizes de implementação a seguir para bloquear uma seção de código ou dados.
O uso principal das rotinas Mm(Un)LockXxx é habilitar código ou dados normalmente nãopagados a serem tornados pagináveis e trazidos como código ou dados nãopagados. Drivers como o driver serial e o driver paralelo são bons exemplos: se não houver identificadores abertos para um dispositivo que um driver gerencia, partes de código não serão necessárias e poderão permanecer paginada. O redirecionador e o servidor também são bons exemplos de drivers que podem usar essa técnica. Quando não há conexões ativas, ambos os componentes podem ser paginado.
Toda a seção paginável está bloqueada na memória.
Uma seção para código e outra para dados por driver é eficiente. Muitas seções nomeadas e pagináveis geralmente são ineficientes.
Mantenha seções puramente pagináveis e seções pagináveis, mas bloqueadas sob demanda, separadas.
Lembre-se de que MmLockPagableCodeSection e MmLockPagableDataSection não devem ser chamados com frequência. Essas rotinas podem causar uma atividade de E/S pesada quando o gerenciador de memória carrega a seção. Se um driver precisar bloquear uma seção de vários locais em seu código, ele deverá usar MmLockPagableSectionByHandle.