Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les pilotes en mode noyau peuvent utiliser la protection d’exécution pour accéder en toute sécurité aux objets de 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 épuisé si tous les accès en attente de 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 avoir besoin d’être exécuté afin 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 contre l'épuisement sur l'objet. Lorsque la protection contre l'arrêt 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 la terminaison de l’accès. Avant le démarrage de l’accès, le pilote d’accès demande une protection contre la dégradation sur l’objet. Pour un objet de longue durée, cette demande est presque toujours accordée. Une fois l’accès terminé, le pilote d’accès libère sa protection d’exécution acquise précédemment sur l’objet.
Routines de protection d’exécution principale
Pour commencer à partager un objet, le pilote propriétaire de l’objet appelle la routine ExInitializeRundownProtection pour initialiser la protection d’exécution sur l’objet. Après cet appel, d'autres pilotes qui accèdent à l'objet peuvent acquérir et libérer la protection contre l'arrêt sur l'objet.
Un pilote qui accède à l’objet partagé appelle la routine ExAcquireRundownProtection pour demander une protection d’exécution sur l’objet. Une fois l’accès terminé, ce pilote appelle la routine ExReleaseRundownProtection pour libérer la protection d’exécution sur l’objet.
Si le pilote propriétaire détermine que l’objet partagé doit être supprimé, ce pilote attend de supprimer l’objet jusqu’à ce que tous les accès en attente de l’objet soient terminés.
En vue de supprimer l’objet partagé, le pilote propriétaire appelle la routine ExWaitForRundownProtectionRelease pour attendre que l’objet s’exécute. Pendant cet appel, ExWaitForRundownProtectionRelease attend que toutes les instances précédemment accordées de protection de liquidation sur l’objet soient libérées, mais empêche que de nouvelles demandes de protection de liquidation sur l’objet soient accordées. Une fois le dernier accès protégé terminé et toutes les instances de protection d’exécution sont publiées, ExWaitForRundownProtectionRelease retourne et le pilote propriétaire peut supprimer l’objet en toute sécurité.
ExWaitForRundownProtectionRelease bloque l’exécution du thread de pilote appelant jusqu’à ce que tous les pilotes qui contiennent la protection d’exécution sur l’objet partagé libèrent cette protection. Pour empêcher ExWaitForRundownProtectionRelease de bloquer l’exécution pendant des périodes excessivement longues, les threads de pilotes qui accèdent à l’objet partagé doivent éviter d’être suspendus pendant qu’ils contiennent une protection d’exécution sur l’objet. Pour cette raison, les pilotes d'accès doivent appeler ExAcquireRundownProtection et ExReleaseRundownProtection dans une région critique ou une région protégée, ou lors de l’exécution à IRQL = APC_LEVEL.
Applications de la protection contre la décharge
La protection d’exécution est utile pour fournir l’accès à un objet partagé qui est presque toujours disponible, mais peut parfois avoir besoin d’ê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 sa suppression. Dans le cas contraire, ces accès non valides peuvent entraîner un comportement imprévisible, une altération des données ou même une défaillance du système.
Par exemple, un pilote antivirus reste généralement chargé en mémoire lorsque le système d’exploitation est en cours d’exécution. Parfois, ce pilote peut nécessiter d'être désinstallé et remplacé par une version mise à jour. D’autres pilotes envoient des demandes d’E/S au pilote antivirus pour accéder aux données et aux routines de ce pilote. Avant d’envoyer une demande d’E/S, un composant noyau, tel qu’un gestionnaire de filtres de système de fichiers, peut acquérir une protection d’exécution pour se protéger contre le déchargement prématuré du pilote antivirus pendant qu’il gère la demande d’E/S. Une fois la demande d’E/S terminée, la protection d’exécution peut être libérée.
La protection d’exécution ne sérialise pas les accès à un objet partagé. Si deux pilotes ou plus qui accèdent peuvent simultanément maintenir une protection de désactivation 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.
Structure EX_RUNDOWN_REF
Une structure EX_RUNDOWN_REF suit l’état de la protection d’exécution sur un objet partagé. Cette structure est opaque pour les pilotes. Les routines de protection d’exécution fournies par le système utilisent cette structure pour compter le nombre d’instances de protection d’exécution actuellement en vigueur sur l’objet. Ces routines utilisent également cette structure pour déterminer si l’objet est exécuté ou est en cours d’exécution.
Pour commencer à partager un objet, le pilote propriétaire 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 pour d’autres pilotes qui nécessitent l’accès à l’objet. Les pilotes d’accès passent cette structure en tant que paramètre aux appels ExAcquireRundownProtection et ExReleaseRundownProtection qui acquièrent et libèrent la protection d’exécution sur l’objet. Le pilote propriétaire transmet cette structure en tant que paramètre à l’appel ExWaitForRundownProtectionRelease qui attend que l’objet s’exécute afin qu’il puisse être supprimé en toute sécurité.
Comparaison des verrous
La protection d’exécution est l’une des différentes façons de garantir un accès sécurisé à un objet partagé. Une autre approche consiste à utiliser un verrou logiciel d’exclusion mutuelle. Si un pilote nécessite l’accès à un objet actuellement verrouillé par un autre pilote, le premier pilote doit attendre que le deuxième pilote libère le verrou. Toutefois, l’acquisition et la libération de verrous peuvent devenir un goulot d’étranglement des performances, et les verrous peuvent consommer de grandes quantités de mémoire. En cas d’utilisation incorrecte, les verrous peuvent provoquer un interblocage des pilotes utilisant les mêmes objets partagés. Les efforts de détection et d’évitement des interblocages nécessitent généralement le détournement de ressources informatiques substantielles.
Contrairement aux verrous, la protection contre l'épuisement a des exigences de temps de traitement et de mémoire relativement légères. Un nombre de références simple est associé à l’objet pour s’assurer que la suppression de l’objet est différée jusqu’à ce que tous les accès en attente de l’objet soient terminés. Avec cette approche, les instructions matérielles interblocées atomiques peuvent être utilisées au lieu de verrous logiciels d’exclusion mutuelle pour garantir un accès sécurisé à un objet. Les appels pour acquérir et libérer la protection contre la dégradation sont généralement rapides. Les avantages de l’utilisation d’un mécanisme léger, comme la protection d'arrêt, peuvent être significatifs pour un objet partagé qui a une longue durée de vie et qui est utilisé par de nombreux pilotes.
Autres procédures de protection dégradées
Plusieurs autres routines de protection d’exécution sont disponibles, en plus de celles mentionnées précédemment. Ces routines supplémentaires peuvent ê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 d’exécution sur cet objet.
La routine ExRundownCompleted met à jour la structure EX_RUNDOWN_REF pour indiquer que l’exécution de l’objet associé est terminée.
Les routines ExAcquireRundownProtectionEx et ExReleaseRundownProtectionEx sont similaires à ExAcquireRundownProtection et ExReleaseRundownProtection. Ces quatre routines incrémentent ou décrémentent le nombre d'instances de protection de fin de course qui sont en vigueur sur un objet partagé. Alors que ExAcquireRundownProtection et ExReleaseRundownProtection incrémentent et décrémentent ce nombre par un, ExAcquireRundownProtectionEx et ExReleaseRundownProtectionEx incrémentent et décrémentent ce nombre par des montants arbitraires.
Protection d’exécution prenant en compte le cache
Une référence d’exécution est une structure de données compacte et rapide, mais elle peut provoquer une contention de cache lorsque de nombreux processeurs essaient d’acquérir la référence en même temps. Cela peut affecter les performances et la scalabilité de votre pilote.
Pour éviter ce problème, vous pouvez utiliser une référence de récapitulation prenant en compte le cache pour répartir le suivi des références sur plusieurs lignes de cache. Cela réduit la contention du cache et améliore les performances de votre pilote sur des ordinateurs multiprocesseurs.
Pour utiliser une référence d’exécution prenant en compte le cache, procédez comme suit :
- Créez un objet EX_RUNDOWN_REF_CACHE_AWARE en effectuant l’une des opérations suivantes :
- Appelez ExAllocateCacheAwareRundownProtection. Cela prend en charge l’initialisation.
- Vous pouvez également contrôler l’allocation de mémoire, appeler ExSizeOfRundownProtectionCacheAware, allouer une mémoire tampon de la taille retournée, puis passer cette mémoire tampon et cette taille à ExInitializeRundownProtectionCacheAware.
- Demandez la protection de rundown 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 désactivation.
- Relâchez la protection de rundown sur l’objet après y avoir accédé en appelant la routine ExReleaseRundownProtectionCacheAware.
- Attendez que l’objet s’exécute avant de le supprimer en appelant la routine ExWaitForRundownProtectionReleaseCacheAware . Cette routine bloque le fil d'exécution actuel jusqu'à ce que toutes les instances de protection d’arrêt de l'objet soient libérées.
- Si le pilote a appelé ExAllocateCacheAwareRundownProtection précédemment, il doit appeler ExFreeCacheAwareRundownProtection pour libérer la référence de réduction.
Pour réutiliser une référence d’exécution prenant en compte le cache, procédez comme suit :
- Après avoir appelé ExWaitForRundownProtectionReleaseCacheAware, appelez ExRundownCompletedCacheAware pour indiquer que l’exécution de l’ancien objet est terminée.
- Appelez ExReInitializeRundownProtectionCacheAware pour réinitialiser la référence après l’exécution de l’objet associé.
- Le pilote peut maintenant appeler à nouveau ExAcquireRundownProtectionCacheAware.
Une référence de rundown optimisée pour le cache présente l’avantage d’améliorer les performances et l’évolutivité dans des situations spécifiques, mais elle consomme plus de mémoire qu’une référence de rundown régulière. Vous devez envisager ce compromis lors du choix entre les deux types de références d’exécution.