Verrouillage du code ou des données paginables

Certains pilotes en mode noyau, tels que les pilotes série et parallèle, n’ont pas besoin d’être en mémoire résidente, sauf si les appareils qu’ils gèrent sont ouverts. Toutefois, tant qu’il existe une connexion ou un port actif, une partie du code du pilote qui gère ce port doit être résidente pour traiter l’appareil. Lorsque le port ou la connexion n’est pas utilisé, le code du pilote n’est pas requis. En revanche, un pilote pour un disque qui contient le code système, le code d’application ou le fichier de pagination système doit toujours être résident en mémoire, car le pilote transfère constamment des données entre son appareil et le système.

Un pilote pour un appareil utilisé de manière sporadique (comme un modem) peut libérer de l’espace système lorsque l’appareil qu’il gère n’est pas actif. Si vous placez dans une seule section le code qui doit être résident pour traiter un appareil actif, et si votre pilote verrouille le code en mémoire pendant l’utilisation de l’appareil, cette section peut être désignée comme paginable. Lorsque le périphérique du pilote est ouvert, le système d’exploitation place la section paginable en mémoire et le pilote la verrouille jusqu’à ce qu’elle ne soit plus nécessaire.

Le code de pilote audio du CD système utilise cette technique. Le code du pilote est regroupé en sections paginables en fonction du fabricant du périphérique CD. Certaines marques peuvent ne jamais être présentes sur un système donné. En outre, même si un CD-ROM existe sur un système, il peut être rarement consulté. Par conséquent, le regroupement du code en sections paginables par type de CD garantit que le code des appareils qui n’existent pas sur un ordinateur particulier ne sera jamais chargé. Toutefois, lorsque l’appareil est accessible, le système charge le code du périphérique CD approprié. Ensuite, le pilote appelle la routine MmLockPagableCodeSection , comme décrit ci-dessous, pour verrouiller son code dans la mémoire pendant l’utilisation de son appareil.

Pour isoler le code paginable dans une section nommée, marquez-le avec la directive de compilateur suivante :

#pragma alloc_text(PAGE*Xxx, *RoutineName)

Le nom d’une section de code paginable doit commencer par les quatre lettres « PAGE » et peut être suivi de jusqu’à quatre caractères (représentés ici sous la forme Xxx) pour identifier la section de manière unique. Les quatre premières lettres du nom de section (autrement dit, « PAGE ») doivent être en majuscules. RoutineName identifie un point d’entrée à inclure dans la section paginable.

Le nom valide le plus court pour une section de code paginable dans un fichier de pilote est simplement PAGE. Par exemple, la directive pragma dans l’exemple de code suivant s’identifie RdrCreateConnection comme point d’entrée dans une section de code paginable nommée PAGE.

#ifdef  ALLOC_PRAGMA 
#pragma alloc_text(PAGE, RdrCreateConnection) 
#endif 

Pour rendre le code de pilote paginable résident et verrouillé dans la mémoire, un pilote appelle MmLockPagableCodeSection, en passant une adresse (généralement le point d’entrée d’une routine de pilote) qui se trouve dans la section du code paginable. MmLockPagableCodeSection verrouille l’ensemble du contenu de la section qui contient la routine référencée dans l’appel. En d’autres termes, cet appel rend chaque routine associée au même identificateur PAGEXxx résidente et verrouillée dans la mémoire.

MmLockPagableCodeSection retourne un handle à utiliser lors du déverrouillage de la section (en appelant la routine MmUnlockPagableImageSection ) ou lorsque le pilote doit verrouiller la section à partir d’emplacements supplémentaires dans son code.

Un pilote peut également traiter les données rarement utilisées comme paginables afin qu’elles puissent également être paginées jusqu’à ce que l’appareil qu’il prend en charge soit actif. Par exemple, le pilote de mélangeur système utilise des données paginables. L’appareil de mixage n’a aucune E/S asynchrone associée, ce pilote peut donc rendre ses données paginables.

Le nom d’une section de données paginables doit commencer par les quatre lettres « PAGE » et peut être suivi de jusqu’à quatre caractères pour identifier de manière unique la section. Les quatre premières lettres du nom de section (autrement dit, « PAGE ») doivent être en majuscules.

Évitez d’affecter des noms identiques aux sections de code et de données. Pour rendre le code source plus lisible, les développeurs de pilotes attribuent généralement le nom PAGE à la section de code paginable, car ce nom est court et il peut apparaître dans de nombreuses directives pragma alloc_text. Les noms plus longs sont ensuite attribués à toutes les sections de données paginables (par exemple, PAGEDATA pour data_seg, PAGEBSS pour bss_seg, etc.) dont le pilote peut avoir besoin.

Par exemple, les deux premières directives pragma de l’exemple de code suivant définissent deux sections de données paginables, PAGEDATA et PAGEBSS. PAGEDATA est déclaré à l’aide de la directive pragma data_seg et contient des données initialisées. PAGEBSS est déclaré à l’aide de la directive pragma bss_seg et contient des données non initialisées.

#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()

Dans cet exemple de code, Variable1 et Array1 sont explicitement initialisés et sont donc placés dans la section PAGEDATA. Variable2 et Array2 sont implicitement initialisés à zéro et sont placés dans la section PAGEBSS.

L’initialisation implicite des variables globales à zéro réduit la taille du fichier exécutable sur le disque et est préférable à l’initialisation explicite à zéro. L’initialisation zéro explicite doit être évitée, sauf dans les cas où elle est nécessaire pour placer une variable dans une section de données spécifique.

Pour rendre une section de données résidente en mémoire et la verrouiller dans la mémoire, un pilote appelle MmLockPagableDataSection, en transmettant un élément de données qui apparaît dans la section de données paginables. MmLockPagableDataSection retourne un handle à utiliser dans les demandes de verrouillage ou de déverrouillage ultérieures.

Pour restaurer les status paginables d’une section verrouillée, appelez MmUnlockPagableImageSection, en transmettant la valeur de handle retournée par MmLockPagableCodeSection ou MmLockPagableDataSection, le cas échéant. La routine Deload d’un pilote doit appeler MmUnlockPagableImageSection pour libérer chaque handle qu’elle a obtenue pour les sections de code et de données verrouillables.

Le verrouillage d’une section est une opération coûteuse, car le gestionnaire de mémoire doit rechercher sa liste de modules chargée avant de verrouiller les pages dans la mémoire. Si un pilote verrouille une section à de nombreux emplacements dans son code, il doit utiliser la plus efficace MmLockPagableSectionByHandle après son appel initial à La sectionXxxMmLockPagable.

Le handle transmis à MmLockPagableSectionByHandle est le handle retourné par l’appel précédent à MmLockPagableCodeSection ou MmLockPagableDataSection.

Le gestionnaire de mémoire conserve un nombre pour chaque handle de section et incrémente ce nombre chaque fois qu’un pilote appelle MmLockPagableXxx pour cette section. Un appel à MmUnlockPagableImageSection décrémente le nombre. Bien que le compteur de n’importe quel handle de section soit différent de zéro, cette section reste verrouillée dans la mémoire.

Le handle d’une section est valide tant que son pilote est chargé. Par conséquent, un pilote ne doit appeler la sectionXxxMmLockPagable qu’une seule fois. Si le pilote nécessite des appels de verrouillage supplémentaires, il doit utiliser MmLockPagableSectionByHandle.

Si un pilote appelle une routine MmLockPagableXxx pour une section déjà verrouillée, le gestionnaire de mémoire incrémente le nombre de références pour la section. Si la section est paginée lorsque la routine de verrouillage est appelée, le gestionnaire de mémoire s’affiche dans la section et définit son nombre de références sur un.

L’utilisation de cette technique réduit l’effet du pilote sur les ressources système. Lorsque le pilote s’exécute, il peut verrouiller en mémoire le code et les données qui doivent être résidentes. Lorsqu’il n’y a aucune demande d’E/S en suspens pour son appareil (c’est-à-dire lorsque l’appareil est fermé ou si l’appareil n’a jamais été ouvert), le pilote peut déverrouiller le même code ou les mêmes données, ce qui le rend disponible pour être paginé.

Toutefois, une fois qu’un pilote a connecté des interruptions, tout code de pilote qui peut être appelé pendant le traitement des interruptions doit toujours être résident de la mémoire. Bien que certains pilotes de périphérique puissent être rendus paginables ou verrouillés dans la mémoire à la demande, certains ensembles principaux du code et des données d’un tel pilote doivent résider en permanence dans l’espace système.

Tenez compte des instructions d’implémentation suivantes pour verrouiller un code ou une section de données.

  • L’utilisation principale des routines Mm(Un)LockXxx est de permettre au code ou aux données non paginées normalement d’être paginables et d’être introduits en tant que code ou données non paginés. Les pilotes tels que le pilote série et le pilote parallèle sont de bons exemples : s’il n’y a pas de poignées ouvertes sur un périphérique géré par un tel pilote, des parties de code ne sont pas nécessaires et peuvent rester paginées. Le redirecteur et le serveur sont également de bons exemples de pilotes qui peuvent utiliser cette technique. En l’absence de connexions actives, ces deux composants peuvent être paginés.

  • Toute la section paginable est verrouillée dans la mémoire.

  • Une section pour le code et une pour les données par pilote sont efficaces. De nombreuses sections nommées et paginables sont généralement inefficaces.

  • Conservez les sections purement paginables et les sections paginées, mais verrouillées à la demande séparément.

  • N’oubliez pas que MmLockPagableCodeSection et MmLockPagableDataSection ne doivent pas être fréquemment appelés. Ces routines peuvent entraîner une activité d’E/S intense lorsque le gestionnaire de mémoire charge la section. Si un pilote doit verrouiller une section à plusieurs emplacements dans son code, il doit utiliser MmLockPagableSectionByHandle.