Protection contre le run-down
À partir de Windows XP, la protection contre le run-down est disponible pour les pilotes en mode noyau. Les pilotes peuvent utiliser cette protection pour accéder de manière sûre aux objets dans la mémoire système partagée qui sont créés et supprimés par un autre pilote en mode noyau.
Un objet est dit en run-down si tous les accès en cours à l’objet sont terminés et qu’aucune nouvelle demande d’accès à l’objet ne sera accordée. Par exemple, un objet partagé peut nécessiter un run down pour qu’il puisse être supprimé et remplacé par un nouvel objet.
Le pilote qui possède l’objet partagé peut permettre à d’autres pilotes d’acquérir et de libérer la protection run-down sur l’objet. Lorsque la protection run-down est en vigueur, un pilote autre que le propriétaire peut accéder à l’objet sans risque que le propriétaire supprime l’objet avant que l’accès ne soit complet. Avant que l’accès ne commence, le pilote accédant demande une protection run-down sur l’objet. Pour un objet de longue durée, cette demande est presque toujours accordée. Après que l’accès soit terminé, le pilote accédant libère la protection run-down qu’il avait précédemment acquise sur l’objet.
Routines de protection run-down principales
Pour commencer à partager un objet, le pilote qui possède l’objet appelle la routine ExInitializeRundownProtection pour initialiser la protection run-down sur l’objet. Après cet appel, d’autres pilotes qui accèdent à l’objet peuvent acquérir et libérer la protection run-down sur l’objet.
Un pilote qui accède à l’objet partagé appelle la routine ExAcquireRundownProtection pour demander une protection run-down sur l’objet. Après que l’accès soit terminé, ce pilote appelle la routine ExReleaseRundownProtection pour libérer la protection run-down sur l’objet.
Si le pilote propriétaire détermine que l’objet partagé doit être supprimé, ce pilote attend pour supprimer l’objet jusqu’à ce que tous les accès en cours à l’objet soient terminés.
En préparation pour supprimer l’objet partagé, le pilote propriétaire appelle la routine ExWaitForRundownProtectionRelease pour attendre que l’objet soit en run down. Durant cet appel, ExWaitForRundownProtectionRelease attend que toutes les instances précédemment accordées de protection run-down sur l’objet soient libérées, mais empêche les nouvelles demandes de protection run-down sur l’objet d’être accordées. Une fois que le dernier accès protégé est terminé et que toutes les instances de protection run-down sont libérées, ExWaitForRundownProtectionRelease retourne, et le pilote propriétaire peut supprimer l’objet en toute sécurité.
ExWaitForRundownProtectionRelease bloque l’exécution du thread du pilote appelant jusqu’à ce que tous les pilotes qui détiennent une protection run-down sur l’objet partagé libèrent cette protection. Pour éviter que ExWaitForRundownProtectionRelease ne bloque l’exécution pendant des périodes excessivement longues, les threads des pilotes qui accèdent à l’objet partagé devraient éviter d’être suspendus pendant qu’ils détiennent une protection run-down sur l’objet. C’est pourquoi les pilotes accédants devraient appeler ExAcquireRundownProtection et ExReleaseRundownProtection dans une région critique ou une région gardée, ou tout en fonctionnant à IRQL = APC_LEVEL.
Utilisations pour la protection run-down
La protection run-down est particulièrement utile pour fournir un accès à un objet partagé qui est presque toujours disponible mais qui pourrait occasionnellement devoir être supprimé et remplacé. Les pilotes qui accèdent aux données ou qui appellent des routines dans cet objet ne doivent pas essayer d’accéder à l’objet après qu’il soit supprimé. Sinon, ces accès invalides pourraient causer un comportement imprévisible, une corruption des données, ou même une défaillance du système.
Par exemple, un pilote antivirus reste typiquement chargé en mémoire lorsque le système d’exploitation fonctionne. Occasionnellement, ce pilote peut devoir être déchargé et remplacé par une version mise à jour du pilote. D’autres pilotes envoient des requêtes E/S au pilote antivirus pour accéder aux données et routines dans ce pilote. Avant d’envoyer une requête E/S, un composant du noyau, tel qu’un gestionnaire de filtre de système de fichiers, peut acquérir une protection run-down pour se protéger contre un déchargement prématuré du pilote antivirus pendant qu’il gère la requête E/S. Après que la requête E/S soit complétée, la protection run-down peut être libérée.
La protection run-down ne sérialise pas les accès à un objet partagé. Si deux pilotes ou plus peuvent simultanément détenir une protection run-down sur un objet, et que les accès à l’objet doivent être sérialisés, un autre mécanisme, tel qu’un verrou d’exclusion mutuelle, doit être utilisé pour sérialiser les accès.
La structure EX_RUNDOWN_REF
Une structure EX_RUNDOWN_REF suit le statut de la protection run-down sur un objet partagé. Cette structure est opaque pour les pilotes. Les routines de protection run-down fournies par le système utilisent cette structure pour compter le nombre d’instances de protection run-down qui sont actuellement en vigueur sur l’objet. Ces routines utilisent également cette structure pour suivre si l’objet est en run down ou est en cours de run down.
Pour commencer à partager un objet, le pilote qui possède l’objet appelle ExInitializeRundownProtection pour initialiser la structure EX_RUNDOWN_REF associée à l’objet. Après l’initialisation, le pilote propriétaire peut rendre cette structure disponible aux autres pilotes qui nécessitent un accès à l’objet. Les pilotes accédants passent cette structure en paramètre aux appels ExAcquireRundownProtection et ExReleaseRundownProtection qui acquièrent et libèrent la protection run-down sur l’objet. Le pilote propriétaire passe cette structure en paramètre à l’appel ExWaitForRundownProtectionRelease qui attend que l’objet soit en run down pour qu’il puisse être supprimé en toute sécurité.
Comparaison avec les verrous
La protection run-down est l’une des nombreuses manières de garantir un accès sûr à un objet partagé. Une autre approche est d’utiliser un verrou logiciel d’exclusion mutuelle. Si un pilote doit pouvoir accéder à un objet qui est actuellement verrouillé par un autre pilote, le premier pilote doit attendre que le second pilote libère le verrou. Cependant, l’acquisition et la libération de verrous peuvent devenir un goulot d’étranglement de performance, et les verrous peuvent consommer de grandes quantités de mémoire. En cas d’utilisation incorrecte, les verrous peuvent causer une concurrence des pilotes pour les mêmes objets partagés. Les efforts de détection et d’évitement des interblocages nécessitent généralement de détourner des ressources informatiques substantielles.
Contrairement aux verrous, la protection run-down a des exigences de temps de traitement et de mémoire relativement légères. Un simple comptage de références est associé à l’objet pour garantir que la suppression de l’objet est différée jusqu’à ce que tous les accès en cours à l’objet soient terminés. Avec cette approche, des instructions matérielles atomiques et verrouillées peuvent être utilisées à la place des verrous logiciels d’exclusion mutuelle pour garantir un accès sûr à un objet. Les appels pour acquérir et libérer la protection run-down sont en général très rapides. Les avantages d’utiliser un mécanisme léger, tel que la protection run-down, peuvent être significatifs pour un objet partagé qui a une longue durée de vie et est partagé entre de nombreux pilotes.
Autres routines de protection run-down
Plusieurs autres routines de protection run-down sont disponibles, en plus de celles qui ont été mentionnées précédemment. Ces routines supplémentaires pourraient être utilisées par certains pilotes.
La routine ExReInitializeRundownProtection permet à une structure EX_RUNDOWN_REF précédemment utilisée d’être associée à un nouvel objet, et initialise la protection run-down sur cet objet.
La routine ExRundownCompleted met à jour la structure EX_RUNDOWN_REF pour indiquer que le run down de l’objet associé est terminé.
Les routines ExAcquireRundownProtectionEx et ExReleaseRundownProtectionEx sont similaires à ExAcquireRundownProtection et ExReleaseRundownProtection. Ces quatre routines incrémentent ou décrémentent le comptage des instances de protection run-down qui sont en vigueur sur un objet partagé. Alors que ExAcquireRundownProtection et ExReleaseRundownProtection incrémentent et décrémentent ce comptage de un, ExAcquireRundownProtectionEx et ExReleaseRundownProtectionEx incrémentent et décrémentent le comptage par des montants arbitraires.
Protection run-down consciente du cache
Une référence run-down est une structure de données compacte et rapide, mais elle peut provoquer une contention de cache lorsque de nombreux processeurs tentent d’acquérir la référence en même temps. Cela peut affecter la performance et l’évolutivité de votre pilote.
Pour éviter ce problème, vous pouvez utiliser une référence run-down consciente du cache pour distribuer le suivi de la référence à travers plusieurs lignes de cache. Cela réduit la contention du cache et améliore la performance de votre pilote sur les ordinateurs multiprocesseurs.
Pour utiliser une référence run-down consciente du cache, suivez cette procédure :
- Créez un objet EX_RUNDOWN_REF_CACHE_AWARE en faisant l’une des actions suivantes :
- Appelez ExAllocateCacheAwareRundownProtection. Notez que cela prend en charge l’initialisation.
- Alternativement, pour contrôler l’allocation de mémoire, appelez ExSizeOfRundownProtectionCacheAware, allouez un tampon de la taille retournée, puis passez ce tampon et sa taille à ExInitializeRundownProtectionCacheAware.
- Demandez la protection run-down sur l’objet avant d’y accéder en appelant la routine ExAcquireRundownProtectionCacheAware. Cette routine retourne TRUE si la demande est accordée, ou FALSE si l’objet est en cours de run down.
- Libérez la protection run-down sur l’objet après y avoir accédé en appelant la routine ExReleaseRundownProtectionCacheAware.
- Attendez que l’objet soit en run down avant de le supprimer en appelant la routine ExWaitForRundownProtectionReleaseCacheAware. Cette routine bloque le thread actuel jusqu’à ce que toutes les instances de protection run-down sur l’objet soient libérées.
- Si le pilote a appelé ExAllocateCacheAwareRundownProtection plus tôt, il devrait appeler ExFreeCacheAwareRundownProtection pour libérer la référence run-down.
Pour réutiliser une référence run-down consciente du cache, suivez cette procédure :
- Après avoir appelé ExWaitForRundownProtectionReleaseCacheAware, appelez ExRundownCompletedCacheAware pour indiquer que le run down de l’ancien objet est terminé.
- Appelez ExReInitializeRundownProtectionCacheAware pour réinitialiser la référence après que l’objet associé soit en run down.
- Le pilote peut alors à nouveau appeler ExAcquireRundownProtectionCacheAware.
Une référence run-down consciente du cache a l’avantage d’une meilleure performance et évolutivité dans certaines situations, mais elle consomme plus de mémoire qu’une référence run-down régulière. Vous devriez considérer ce compromis lors du choix entre les deux types de références run-down.