LA CLOCHE

Il y a ceux qui ont lu cette nouvelle avant vous.
Abonnez-vous pour recevoir les derniers articles.
Email
Nom
Nom de famille
Comment voulez-vous lire The Bell
Pas de spam

Pendant l'exécution du code en mode noyau avec la priorité la plus élevée, aucun autre code sur ce processeur ne peut démarrer. Bien entendu, si de trop grandes quantités de code de programme sont exécutées à des valeurs IRQL trop élevées, cela conduira inévitablement à une dégradation générale du système.

Pour résoudre ce type de problème, le code en mode noyau doit être conçu pour éviter un fonctionnement prolongé à des IRQL plus élevés. L'un des composants les plus importants de cette stratégie est les appels de procédure différés (DPC) - appels de procédure différés.

Fonctionnement DPC

Le schéma d'utilisation des appels de procédure différés vous permet de construire le processus d'exécution de manière à ce que la tâche puisse être prévu code fonctionnant à un niveau IRQL élevé, mais ce n'est pas encore exécuté ... Ce report est utile s'il existe un service d'interruption dans le pilote et qu'il n'y a aucune raison de bloquer l'exécution d'un autre code à un IRQL inférieur. En d'autres termes, lorsque le traitement de cette situation peut être reporté sans douleur à une date ultérieure.

Le système d'exploitation maintient une file d'attente d'objets DPC pour suivre les demandes d'appel de procédures DPC.

Pour commencer, nous nous limiterons à considérer un cas plus simple de travail avec des procédures DPC conçues pour être utilisées en conjonction avec des procédures de gestion des interruptions. Ce type de procédures DPC a reçu un nom spécial dans la littérature DpcForIsr.

L'objet DPC à utiliser dans les routines d'interruption est créé à l'appel IoInitializeDpcRequestgénéralement effectuée dans les procédures de démarrage du pilote. Cet appel enregistre la procédure proposée par le pilote DpcForIsr et l'associe à l'objet en cours de création - une technique assez courante sous Windows. Il faut surtout noter que l'objet DPC créé par cet appel restera dans les entrailles du système d'exploitation, inaccessible au développeur du pilote. (La différence entre DpcForIsr et les autres procédures DPC est seulement que ces dernières sont gérées par des appels Ke ... Dpcet les objets DPC créés pour eux sont disponibles pour le développeur du pilote.)

Si le pilote a enregistré sa procédure DpcForIsr, alors pendant le traitement d'interruption ISR par la procédure, l'objet DPC correspondant peut être placé dans la file d'attente système DPC (en fait, une demande d'appel de cette procédure DpcForIsr plus tard) - en appelant IoRequestDpc... La procédure DpcForIsr achèvera plus tard le traitement de la demande reçue par l'ISR par la procédure, qui sera effectuée dans des conditions moins critiques et à un IRQL bas.

De manière générale, le fonctionnement des procédures DPC (dans ce cas, DpcForIsr) se compose des opérations suivantes:

  • Lorsqu'un morceau de code s'exécutant à un IRQL élevé (matériel) veut planifier une partie de son travail à effectuer à un IRQL faible, il ajoute un objet DPC à la file d'attente des appels de procédure en attente du système.
  • Tôt ou tard, l'IRQL du processeur tombe sous DISPATCH_LEVEL et le travail qui a été retardé par l'interruption est servi par la fonction DPC. Le répartiteur DPC récupère chaque objet DPC de la file d'attente et appelle la fonction appropriée, le pointeur vers lequel est stocké dans cet objet. Cette fonction est appelée pendant que le processeur fonctionne à DISPATCH_LEVEL.

Caractéristiques du mécanisme DPC

En général, travailler avec des appels de procédure différés n'est pas difficile car les systèmes d'exploitation Windows 2000 / XP / Server 2003 offrent un grand nombre d'appels système qui cachent une grande partie des détails du processus. Cependant, deux des aspects les plus décevants du travail avec DPC doivent être mis en évidence.

Tout d'abord, Windows NT 5 impose une limitation selon laquelle une seule instance d'un objet DPC peut être placée dans la file d'attente DPC système à un certain intervalle de temps. Les tentatives de placer un objet dans la file d'attente DPC qui correspond exactement à celui qui y est déjà présent sont rejetées. Par conséquent, un seul appel à la procédure DPC se produit, même si le pilote attend la fin de deux appels. Cela peut se produire si deux interruptions ont été déclenchées par le dispositif en cours de maintenance et si le traitement du premier appel procédural différé n'a pas encore commencé. La première instance de l'objet DPC est toujours dans la file d'attente, tandis que le pilote a déjà commencé à traiter la deuxième interruption.

La conception du pilote devrait prévoir une telle évolution du mécanisme DPC. Peut-être qu'un compteur de requêtes DPC supplémentaire devrait être fourni, ou le pilote peut implémenter sa propre implémentation de la file d'attente de requêtes. Au moment de la procédure différée réelle, vous pouvez vérifier le compteur et votre propre file d'attente de demandes pour déterminer le travail spécifique à effectuer.

Deuxièmement, il existe des problèmes de synchronisation importants lorsque vous travaillez sur des plates-formes multiprocesseurs. Supposons qu'un code de programme s'exécutant sur un processeur effectue un service d'interruption et planifie un appel de procédure DPC (en plaçant un objet DPC dans la file d'attente du système). Cependant, même avant que l'interruption ne soit complètement traitée, un autre processeur peut commencer à traiter l'objet DPC placé dans la file d'attente système. Ainsi, une situation se présente dans laquelle le code de service d'interruption est exécuté en parallèle et simultanément avec le code de procédure DPC. Pour cette raison, il est nécessaire de prévoir des mesures pour une synchronisation fiable de l'accès du code de programme de procédure DPC aux ressources utilisées conjointement avec la procédure de service d'interruption de pilote.

Si vous regardez attentivement la liste des paramètres d'appel IoInitializeDpcRequest et IoRequestDpc (conçu pour fonctionner avec les procédures DpcForIsr), il est facile de voir que l'objet DPC est "lié" à l'objet périphérique. Lorsque vous placez cet objet dans la file d'attente DPC au moment de la procédure ISR, l'objet périphérique est également indiqué. Ceci permet d'obtenir une certitude, l'appel auquel la procédure DPC particulière est "ordonnée" par la procédure ISR (corrélation par objet dispositif). Cela suggère également qu'un pilote qui a créé plusieurs objets périphérique (un cas plutôt rare) peut également exploiter plusieurs procédures DpcForIsr - une pour chaque objet périphérique.

Le mécanisme DPC système empêche le traitement simultané des objets DPC à partir de la file d'attente système, même dans les configurations multiprocesseurs. Ainsi, si les ressources sont partagées par plusieurs procédures différées, il n'y a pas lieu de s'inquiéter de la synchronisation de l'accès à celles-ci.

Ce qui précède décrit l'utilisation des procédures DPC pour terminer la gestion des interruptions, c'est-à-dire DpcForIsr. Cependant, les procédures DPC peuvent être utilisées d'une autre manière, par exemple en conjonction avec des minuteries pour organiser l'attente. Pour ce faire, un objet DPC sera créé à l'aide de l'appel KeInitializeDPCqui lie cet objet à la procédure DPC incluse avec le pilote. Après cela, vous pouvez définir le délai d'expiration dans l'initialisation précédemment (en utilisant KeInitializeTimer ou KeInitializeEx) objet timer. L'appel est utilisé pour définir l'intervalle d'attente KeSetTimer, auquel, comme l'un des paramètres, un pointeur vers un objet DPC initialisé doit être passé. Lorsque le délai DPC expire, l'objet est ajouté à la file d'attente DPC système et la procédure DPC qui lui est associée est appelée dès que possible. Les DPC de ce second type sont appelés dans la documentation DDK "DPC personnalisé". (Cette utilisation des routines DPC les rend très similaires aux appels APC en mode utilisateur.)

Pour placer dans la file d'attente DPC système des objets correspondant au deuxième type de procédures DPC (non liées aux interruptions), utilisez l'appel KeInsertQueueDpc... En conséquence, le code initiateur appelant doit fonctionner à un niveau IRQL d'au moins DISPATCH_LEVEL.

Pour effacer la file d'attente DPC système des procédures DPC personnalisées, par exemple, si le pilote doit se terminer d'urgence, appelez KeRemoveQueueDpcqui peut être appelé à partir de n'importe quel code de niveau IRQL.

Gestion des interruptions de la minuterie

Chaque ordinateur possède une horloge matérielle ou une horloge système qui génère une interruption matérielle à intervalles fixes. L'intervalle de temps entre les interruptions adjacentes est appelé un tick de processeur ou simplement un tick (tick CPU, tick d'horloge). En règle générale, le minuteur système prend en charge plusieurs valeurs de graduation, mais sous UNIX, cette valeur est généralement définie sur 10 millisecondes, bien que cette valeur puisse différer selon les versions du système d'exploitation. La plupart des systèmes stockent cette valeur dans la constante HZ, qui est définie dans le fichier d'en-tête Par exemple, pour un tick de 10 millisecondes, la valeur HZ est définie sur 100.

Le gestionnaire d'interruption du noyau est appelé par une interruption d'horloge matérielle, qui est généralement la priorité la plus élevée. Ainsi, la gestion des interruptions devrait prendre un minimum de temps. En général, le gestionnaire résout les tâches suivantes:

1. Mise à jour des statistiques d'utilisation du processeur pour le processus en cours

2. Exécution d'un certain nombre de fonctions liées à la planification des processus, telles que le recalcul des priorités et la vérification de la plage horaire d'un processus

3. Vérifier si le quota de processeur pour ce processus a été dépassé et envoyer le signal SIGXCPU à ce processus si

4. Mise à jour de l'heure du système (heure du jour) et des autres minuteries associées

5. Gestion des appels retardés

6. Gestion des alarmes

7. Réveillez, si nécessaire, les processus système, tels que le gestionnaire de pages et le swap

Certaines tâches ne doivent pas être effectuées à chaque tick. La plupart des systèmes introduisent une notation de graduation majeure, qui se produit à chaque graduation, en fonction de la version spécifique du système. Un certain ensemble de fonctions n'est exécuté que sur les graduations majeures. Par exemple, il recalcule les priorités tous les 4 ticks, et SVR4 traite et réveille les processus système une fois par seconde McKusick M.K., Neville-Neil J.W. . FreeBSD: architecture et implémentation . - M.: KUDITS-OBRAZ, 2006 .-- 800 p ...

Appels retardés

Un appel différé définit une fonction qui sera appelée par le noyau après un certain temps. Par exemple, dans SVR4, n'importe quel sous-système du noyau peut enregistrer un appel en attente comme ceci:

int co_ID \u003d timeout (void (* fn) (), caddr_t arg, long delta)

où fn définit l'adresse de la fonction qui doit être appelée, tandis que l'argument arg lui sera passé, et l'appel lui-même sera effectué via des graduations delta. Le noyau fait un appel à fn () dans le contexte système, de sorte que la fonction d'appel différé ne doit pas accéder à l'espace d'adressage du processus en cours (puisqu'elle n'a rien à voir avec lui), et ne doit pas non plus entrer dans un état de veille.

Les appels différés sont utilisés pour exécuter de nombreuses fonctions, par exemple:

1. Exécution d'un certain nombre de fonctions du sous-système de planification et de gestion de la mémoire.

2. Exécution d'un certain nombre de fonctions de pilote de périphérique pour des événements qui sont peu susceptibles de se produire. Un exemple est le module de protocole TCP, qui implémente ainsi la retransmission des paquets réseau par timeout.

3. Périphériques d'interrogation qui ne prennent pas en charge les interruptions.

Notez que les fonctions d'appel différé sont exécutées dans le contexte système, pas dans le contexte d'interruption. Ces fonctions ne sont pas appelées par le gestionnaire d'interruption du minuteur, mais par un gestionnaire d'appels en attente distinct qui est exécuté après le traitement de l'interruption du minuteur. Lors du traitement d'une interruption de temporisation, le système vérifie s'il est nécessaire de démarrer certaines fonctions d'appel différé et définit le drapeau approprié pour celles-ci. À son tour, le gestionnaire d'appels différés vérifie les drapeaux et lance le nécessaire dans le contexte système McKusick MK, Neville-Neil JV FreeBSD: architecture et implémentation. - M.: KUDITS-OBRAZ, 2006 .-- 800 p ...

Contexte du processus

Le contexte de processus comprend le contenu de l'espace d'adressage de tâche alloué au processus, ainsi que le contenu des registres matériels liés au processus et des structures de données du noyau. D'un point de vue formel, le contexte de processus combine le contexte utilisateur, le contexte de registre et le contexte système.

Le contexte utilisateur se compose des instructions et des données du processus, de la pile de tâches et du contenu de l'espace mémoire partagé dans les adresses virtuelles du processus. Les parties de l'espace d'adressage virtuel du processus qui sont périodiquement absentes de la RAM en raison du déchargement ou du remplacement de page sont également incluses dans le contexte utilisateur.

Le contexte de registre se compose des éléments suivants:

1. Un compteur d'instructions indiquant l'adresse de la prochaine instruction à exécuter par le processeur central; cette adresse est une adresse virtuelle dans l'espace noyau ou dans l'espace tâche.

2. Registre d'état du processeur (PS), qui indique l'état du matériel de la machine par rapport au processus. Le registre PS, par exemple, contient généralement des sous-champs qui indiquent si le résultat du dernier calcul est nul, positif ou négatif, si le registre est débordé avec l'ensemble du bit de report, etc. Les opérations affectant le réglage du registre PS sont effectuées pour un processus séparé, car puis le registre PS contient l'état matériel de la machine par rapport au processus. D'autres sous-champs importants du registre PS indiquent le niveau d'interruption actuel du processeur, ainsi que les modes d'exécution actuel et précédent du processus (mode noyau / tâche). La valeur du sous-champ du mode d'exécution actuel du processus détermine si le processus peut exécuter des commandes privilégiées et accéder à l'espace d'adressage du noyau.

3. Pointeur du haut de la pile, qui contient l'adresse de l'élément suivant du noyau ou de la pile de tâches, conformément au mode d'exécution du processus. En fonction de l'architecture de la machine, le pointeur de pile pointe vers le prochain élément de pile libre ou le dernier élément utilisé. Le sens d'augmentation de la pile (vers des adresses supérieures ou inférieures) dépend également de l'architecture de la machine, mais pour nous ces questions sont désormais insignifiantes.

4. Registres à usage général, qui contiennent des informations générées par le processus pendant son exécution.

Le contexte système d'un processus a une "partie statique" (les trois premiers éléments de la liste ci-dessous) et une "partie dynamique" (les deux derniers éléments). Tout au long de son temps d'exécution, un processus a à tout moment une partie statique du contexte système, mais il peut avoir un nombre variable de parties dynamiques. La partie dynamique du contexte système peut être représentée comme une pile dont les éléments sont des niveaux de contexte qui sont poussés sur la pile par le noyau ou sortis de la pile lorsque divers événements se produisent. Le contexte système comprend les composants suivants:

1. Une entrée dans la table des processus décrivant l'état du processus (section 6.1) et contenant diverses informations de contrôle auxquelles le noyau peut toujours accéder.

2. La partie de l'espace d'adressage de tâche allouée au processus, qui stocke les informations de contrôle sur le processus, disponibles uniquement dans le contexte du processus. Les paramètres de contrôle généraux tels que la priorité du processus sont stockés dans la table de processus car ils doivent être accessibles en dehors du contexte du processus.

3. Les entrées de la table des zones de processus privées, des tables de zones générales et des tables de pages, nécessaires pour convertir les adresses virtuelles en adresses physiques, et donc décrire la commande, les données, la pile et d'autres zones du processus. Si plusieurs processus partagent des zones communes, ces zones font partie du contexte de chaque processus, puisque chaque processus fonctionne avec ces zones indépendamment des autres processus. Les tâches de gestion de la mémoire comprennent l'identification des parties non résidentes de la mémoire de l'espace d'adressage virtuel d'un processus.

4. Pile du noyau, qui stocke les enregistrements des procédures du noyau si le processus s'exécute en mode noyau. Bien que tous les processus utilisent les mêmes programmes du noyau, chaque processus a sa propre copie de la pile du noyau pour stocker les appels individuels aux fonctions du noyau. Par exemple, supposons qu'un processus appelle la fonction creat et s'arrête en attendant qu'un nouvel index soit attribué, et qu'un autre processus appelle la fonction de lecture et s'arrête en attendant que le transfert disque-mémoire soit terminé. Les deux processus accèdent aux fonctions du noyau, et chacun d'eux a une pile distincte, qui stocke la séquence des appels effectués. Le noyau doit être capable de restaurer le contenu de la pile du noyau et la position du pointeur de la pile afin de reprendre l'exécution d'un processus en mode noyau. Sur divers systèmes, la pile du noyau est souvent située dans l'espace de processus, mais cette pile est logiquement indépendante et peut donc tenir dans une zone de mémoire distincte. Lorsqu'un processus s'exécute en mode tâche, sa pile de noyau correspondante est vide.

5. La partie dynamique du contexte système du processus, constituée de plusieurs niveaux et ayant la forme d'une pile, qui se libère des éléments dans l'ordre inverse de leur arrivée. Chaque niveau du contexte système contient les informations nécessaires pour restaurer le niveau précédent, y compris le contexte de registre du niveau précédent.

Le noyau pousse le niveau de contexte sur la pile lorsqu'une interruption se produit, lorsqu'une fonction système est appelée ou lorsqu'un contexte de processus change. La couche de contexte est extraite de la pile lorsqu'une interruption est traitée, lorsque le processus revient en mode tâche après avoir exécuté une fonction système ou lorsqu'un changement de contexte est effectué. Ainsi, le changement de contexte implique à la fois de pousser le niveau de contexte sur la pile et de faire sauter le niveau de la pile: le noyau pousse le niveau de contexte de l'ancien processus sur la pile et fait apparaître le niveau de contexte du nouveau processus de la pile. Les informations requises pour restaurer le niveau de contexte actuel sont stockées dans l'entrée de table de processus Robachevsky A.M. UNIX operating system . - SPb.: BHV - Pétersbourg , 2002. -- 528 de . .

Bien que la plupart des interruptions soient générées au niveau matériel, le noyau Windows génère des interruptions logicielles pour diverses tâches, notamment les suivantes:

  • lancement de la distribution de threads;
  • traitement des interruptions non critiques dans le temps;
  • gestion de l'expiration du minuteur;
  • exécution asynchrone d'une procédure dans le contexte d'un thread spécifique;
  • prise en charge des opérations d'E / S asynchrones.

Ces tâches sont décrites dans les sections suivantes.

Interruptions distribuées ou interruptions d'un appel de procédure différé (DeferredProcedureCall, DPC). Lorsqu'un thread ne peut plus continuer l'exécution, peut-être parce qu'il s'est terminé ou parce qu'il est entré volontairement dans un état d'attente, le noyau appelle directement le répartiteur pour changer de contexte immédiatement.

Mais parfois, le noyau découvre que la replanification doit se produire lorsque le code en cours d'exécution est profondément imbriqué. Dans une telle situation, le noyau demande l'envoi, mais le reporte jusqu'à ce qu'il termine son travail actuel. L'utilisation d'une interruption DPC logicielle est un moyen pratique d'implémenter ce délai.

Le noyau élève toujours l'IRQL du processeur vers DPC / dispatch ou supérieur lorsqu'il a besoin de synchroniser l'accès aux structures de noyau communes. Cela bloque les interruptions logicielles supplémentaires et la répartition des threads. Lorsque le noyau détecte qu'une répartition est nécessaire, il demande une interruption DPC / répartition, mais le processeur retarde l'interruption car l'IRQL est à ou au-dessus de ce niveau.

Lorsque le noyau quitte son travail actuel, le processeur voit qu'il est sur le point de pousser l'IRQL sous le niveau DPC / dispatch et vérifie les interruptions de répartition en attente. Si de telles interruptions sont présentes, l'IRQL est abaissé au niveau DPC / dispatch et les interruptions de répartition sont gérées. L'activation d'un répartiteur de thread à l'aide d'une interruption logicielle est un moyen de différer la distribution jusqu'à ce que les bonnes circonstances soient réunies. Mais Windows utilise également des interruptions logicielles pour différer d'autres types de traitement.

En plus de la distribution des threads, le noyau gère l'IRQL et les appels de procédure différés (DPC) à ce niveau. DPC est une fonction qui exécute une tâche système dont le temps est moins critique que la tâche en cours.

Les fonctions sont appelées différées car elles ne nécessitent pas d'exécution immédiate.

Les appels de procédure différés permettent au système d'exploitation de générer une interruption et d'exécuter une fonction système en mode noyau.

Le noyau utilise des appels DPC pour traiter les expirations des minuteries (et les threads libres qui attendent l'expiration des minuteries) et pour replanifier l'utilisation du processeur après l'expiration du temps alloué au thread (quantum de thread). Les pilotes de périphériques utilisent des appels DPC pour gérer les interruptions. Pour assurer un service rapide des interruptions logicielles, Windows utilise des pilotes de périphérique pour maintenir les IRQL sous les IRQL des périphériques. L'un des moyens d'atteindre cet objectif consiste à effectuer le travail minimum nécessaire par les ISR du pilote de périphérique pour notifier leurs périphériques, maintenir un état d'interruption temporaire et une latence de transmission de données, ou traiter d'autres interruptions moins urgentes pour l'exécution dans les appels DPC au niveau DPC / IRQL. envoi.

Un appel DPC est représenté par un objet DPC, qui est un objet de contrôle du noyau qui est invisible pour les programmes en mode utilisateur, mais visible pour les pilotes de périphérique et tout autre code système. Les informations les plus importantes contenues dans l'objet DPC sont l'adresse de la fonction système que le noyau appellera lors de la gestion de l'interruption DPC. Les DPC en attente sont stockés dans des files d'attente gérées par le noyau, une file d'attente pour chaque processeur.

Celles-ci sont appelées files d'attente DPC. Pour demander un DPC, le code système appelle le noyau pour initialiser l'objet DPC, puis place cet objet dans la file d'attente DPC.

Par défaut, le noyau place des objets DPC à la fin de la file d'attente DPC du processeur sur lequel le DPC a été demandé (généralement le processeur sur lequel l'ISR s'exécute). Toutefois, un pilote de périphérique peut ignorer ce comportement en spécifiant une priorité DPC (faible, moyenne, supérieure à moyenne ou élevée, qui par défaut à une priorité moyenne) et en ciblant le DPC sur un processeur spécifique. Un appel DPC qui cible un processeur spécifique est appelé DPC cible. Si le DPC a une priorité élevée, le noyau place l'objet DPC en tête de la file d'attente, sinon il place l'objet à la fin de la file d'attente pour toutes les autres priorités.

Le noyau gère les appels DPC lorsque l'IRQL du processeur est sur le point de passer du DPC / de l'IRQL de répartition ou supérieur à un IRQL inférieur (APC ou passif). Windows maintient l'IRQL au niveau DPC / dispatch et récupère les objets DPC de la file d'attente du processeur actuel jusqu'à ce qu'il soit épuisé (c'est-à-dire que le noyau «consomme» la file d'attente), appelant chaque fonction DPC à son tour. Le noyau permettra au niveau IRQL de tomber en dessous du niveau DPC / dispatch et permettra à l'exécution normale des threads de se poursuivre uniquement lorsque la file d'attente est épuisée. Le traitement DPC est illustré dans la figure Les priorités DPC peuvent affecter le comportement du système d'autres manières.

En règle générale, le noyau lance la consommation d'une file d'attente DPC avec une interruption DPC / répartition. Le noyau génère une telle interruption uniquement lorsque l'appel DPC est dirigé vers le processeur actuel (celui sur lequel l'ISR s'exécute) et que le DPC a une priorité supérieure à faible. Si le DPC a une faible priorité, le noyau ne demandera une interruption que lorsque le nombre de requêtes DPC du processeur en attente dépasse le seuil, ou si le nombre d'appels DPC demandés sur le processeur dans une fenêtre temporelle donnée est faible.

Livraison d'appels DPC

Si l'appel DPC cible un CPU différent de celui exécutant l'ISR et que la priorité DPC est élevée ou moyenne-élevée, le noyau signale immédiatement le CPU cible (en lui envoyant un IPI de répartition) à consommation de sa file DPC, mais uniquement si le processeur cible est inactif. Si la priorité est moyenne ou faible, le nombre d'appels DPC mis en file d'attente sur le processeur cible doit dépasser un certain seuil pour que le noyau émette une interruption DPC / de répartition. Le thread inactif du système vide également la file d'attente DPC du processeur sur lequel il s'exécute. Malgré la flexibilité offerte au système par les cibles d'appel et les niveaux de priorité DPC, les pilotes de périphériques ont rarement besoin de modifier le comportement par défaut de leurs objets DPC. Les situations de vidage de file d'attente DPC sont résumées dans le tableau. Si vous regardez les règles de génération, alors, en fait, il s'avère que les priorités supérieures à la moyenne et élevées sont égales les unes aux autres. La différence apparaît lorsqu'ils sont insérés dans la liste, avec des interruptions de haut niveau à l'avant et des interruptions supérieures à la moyenne à l'arrière.

Étant donné que les threads en mode utilisateur s'exécutent à un IRQL faible, les chances sont élevées que l'appel DPC interrompe le thread utilisateur normal. Les procédures DPC sont exécutées quel que soit le thread en cours d'exécution, par conséquent, lorsque la procédure DPC est démarrée, il ne peut pas faire d'hypothèse sur l'espace d'adressage de quel processus est actuellement affiché. Les routines DPC peuvent appeler des fonctions du noyau, mais elles ne peuvent pas appeler des services système, générer des erreurs de sortie de page ou créer ou attendre des objets de répartition. Cependant, ils peuvent accéder aux adresses non paginées dans la mémoire système car l'espace d'adressage système est toujours mappé, quel que soit le processus en cours.

Règles de génération d'interruptions DPC.

Priorité DPCL'appel DPC cible le processeur en cours d'exécution
Procédure ISR
L'appel DPC cible
vers un autre processeur
FaibleLa longueur de la file d'attente DPC dépasse la longueur maximale de la file d'attente DPC ou le niveau de demande DPC est inférieur au minimum
Niveau de demande DPC
Milieu
(Moyen)
Est toujoursLa longueur de la file d'attente DPC dépasse la longueur maximale de la file d'attente DPC ou le système est inactif
Au-dessus de la moyenne
(Moyen-élevé)
Est toujours
HauteEst toujoursLe processeur cible est inactif

Les appels DPC sont fournis principalement aux pilotes de périphériques, mais sont également utilisés par le noyau. Le plus souvent, le noyau utilise DPC pour gérer l'expiration des tranches de temps. Chaque fois que l'horloge système passe, une interruption se produit au niveau de l'horloge IRQL. Le gestionnaire d'interruption d'horloge (exécuté au niveau de l'horloge IRQL) met à jour l'heure système, puis décrémente le compteur qui suit le temps d'exécution du thread actuel.

Lorsque le compteur atteint zéro, la tranche de temps du thread expirera et le noyau devra peut-être replanifier le temps du processeur, c'est-à-dire exécuter une tâche avec une priorité inférieure, qui devrait être effectuée au niveau DPC / dispatch IRQL.

Le gestionnaire d'interruption d'horloge met en file d'attente un appel DPC pour lancer la distribution de thread, puis quitte et abaisse l'IRQL du processeur. Étant donné que les interruptions DPC ont une priorité inférieure aux interruptions de périphérique, toutes les interruptions de périphérique en attente qui se produisent avant la fin de l'interruption d'horloge sont traitées avant l'émission de l'interruption DPC.

Étant donné que les appels DPC sont exécutés quel que soit le thread en cours d'exécution sur le système (ce qui ressemble beaucoup à des interruptions), ils sont la principale raison pour laquelle le système sur lequel ils s'exécutent devient immunisé contre la charge de travail des systèmes clients, ou poste de travail car même les threads les plus prioritaires seront interrompus par l'appel DPC en attente.

Certains appels DPC prennent tellement de temps que les utilisateurs peuvent remarquer un décalage vidéo ou audio, ou même rencontrer un ralentissement anormal de la réponse de la souris ou du clavier.Par conséquent, pour les pilotes avec de longs appels DPC, Windows prend en charge la diffusion des appels DPC.

Les DPC de streaming, comme leur nom l'indique, sont conçus pour exécuter une procédure DPC à un niveau passif dans un flux avec une priorité en temps réel (priorité 31). Cela permet à l'appel DPC d'avoir la priorité sur la plupart des threads en mode utilisateur (puisque la plupart des threads d'application ne démarrent pas dans des plages de priorité en temps réel). Mais cela permet à d'autres interruptions, appels DPC non-threads, appels APC et threads de priorité plus élevée d'avoir la priorité sur cette procédure.

En plus d'être utilisé pour exécuter le répartiteur de NT (planificateur), IRQL dispatch_level est également utilisé pour gérer les appels de procédure différés (DPC). Les appels DPC sont des rappels vers des sous-programmes qui seront exécutés au niveau de répartition IRQL. Les appels DPC sont généralement demandés à des IRQL supérieurs pour effectuer un traitement prolongé et non critique.

Jetons un coup d'œil à quelques exemples d'utilisation de DPC. Les pilotes de périphériques Windows NT effectuent très peu de traitement dans leurs routines de service d'interruption. Au lieu de cela, lorsqu'un périphérique est interrompu (au niveau DIRQL) et que son pilote détermine qu'un traitement complexe est nécessaire, le pilote demande un DPC. La requête DPC provoque le rappel d'une fonction de pilote spécifique dans le niveau de répartition IRQL pour effectuer le reste du traitement requis. En faisant cela IRQL dispatch_level, le pilote passe moins de temps au niveau DIRQL, et donc réduit la latence d'interruption pour tous les autres périphériques du système.

En figue. 15 montre une séquence typique d'événements.

L'ISR demande d'abord le DPC et NT met en file d'attente l'objet DPC sur la file d'attente du processeur cible. En fonction de la priorité DPC et de la longueur de la file d'attente DPC, NT génère une interruption logicielle DPC immédiatement ou après un certain temps. Lorsque le processeur efface la file d'attente DPC, l'objet DPC quitte la file d'attente et le contrôle passe à sa fonction DPC, qui termine l'interruption en lisant les données du périphérique ou en écrivant des données sur le périphérique qui a généré l'interruption.
Les routines de minuterie sont une autre utilisation courante des DPC. Un pilote peut demander l'exécution d'une fonction spécifique pour le notifier lorsqu'un certain laps de temps s'est écoulé (cela se fait à l'aide de la fonction KeSetTimer ()). Le gestionnaire d'interruption d'horloge surveille le passage du temps et, après un certain laps de temps, demande au DPC pour la routine définie par le pilote. L'utilisation du DPC pour la notification de minuterie permet au gestionnaire d'interruption d'horloge de revenir rapidement, mais entraîne toujours l'appel d'une routine spécifiée sans retard indu.

Objets DPC

Un appel DPC est décrit par un objet DPC. La définition de l'objet DPC (KDPC) est faite dans ntddk.h et est illustrée à la Fig. seize.

Figure: 16. Objet DPC

Un objet DPC peut être alloué par le pilote à partir de n'importe quel espace non paginé (tel qu'un pool non paginé). Les objets DPC sont initialisés à l'aide de la fonction KelnitializeDpc (), dont le prototype est:

VOID KelnitializeDpc (IN PKDPC Dpc,
IN PKDEFERRED ^ ROUTINE DeferredRoutine,
IN PVOID DeferredContext);

Où:
Dpc - Pointeur vers l'objet DPC à initialiser; DeferredRoutine - un pointeur vers une fonction par laquelle un appel différé doit être effectué au niveau IRQL DISPATCH_LEVEL. Le prototype de la fonction DeferredRoutine est le suivant:

VOID (* PKDEFERRED_ROUTINE) (
DANS PKDPC Dpc,
DANS PVOID DeferredContext,
IN PVOID SystemArgumentI,
IN PVOID SystemArgument2);

Où:
DeferredContext - la valeur à transmettre à DeferredRoutine en tant que paramètre, avec un pointeur vers l'objet DPC et deux paramètres supplémentaires.
Une demande d'exécution d'un sous-programme DPC spécifique est faite en plaçant un objet DPC décrivant ce sous-programme DPC dans la file d'attente DPC du processeur spécifié, puis en demandant (généralement) un IRQL
dispatch_level. Il existe une file d'attente DPC par processeur. La CPU vers laquelle l'objet DPC est mis en file d'attente est généralement le processeur actuel sur lequel la demande (d'interruption) est émise. La manière dont le processeur est sélectionné pour un DPC particulier est abordée plus loin dans la section Caractéristiques des objets DPC. L'objet DPC est mis en file d'attente à l'aide de la fonction KelnsertQueueDpc (), dont le prototype est:

VOID KelnsertQueueDpc (IN PKDPC Dpc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);

Où:
Dpc - Indique l'objet DPC à mettre en file d'attente;
SystemArgumentl, SystemArgument2 - valeurs arbitraires qui doivent être transmises à la fonction DeferredRoutme en tant que paramètres 3 et 4, respectivement, avec un pointeur vers l'objet DPC et le paramètre DeferredContext spécifié lorsque l'objet DPC a été initialisé.

Activation et maintenance DPC

L'origine d'une interruption logicielle Dispatch_level est reconnue lorsque cette interruption devient l'événement IRQL en attente le plus élevé en attente sur ce processeur. Ainsi, après avoir appelé la fonction KelnsertQueueDpc (), généralement la prochaine fois que le processeur est prêt à revenir à l'IRQL en dessous de dispatch_level, il retournera à la place à l'IRQL dispatch_level et tentera de traiter le contenu de la file d'attente DPC.
Comme indiqué précédemment dans ce chapitre, IRQL DISPATCHJLEVEL est utilisé à la fois pour la distribution et le traitement de la file d'attente DPC. Sur NT 4.0, lorsqu'une interruption DISPATCH_LEVEL est gérée, la file d'attente DPC entière est d'abord desservie, puis le répartiteur est appelé pour planifier l'exécution du thread suivant. Cela est raisonnable car le traitement effectué par la routine DPC pourrait modifier l'état de la base de données de planification de thread, par exemple, rendre le thread précédemment en attente sain.
La file d'attente DPC est gérée par le Microkernel. Chaque fois qu'une file d'attente DPC est traitée, tous les éléments de file d'attente DPC pour le processeur actuel sont traités. Un à la fois, le Microkernel supprime l'objet DPC de la tête de la file d'attente et appelle le DeferredRoutine spécifié dans l'objet. Le micro-noyau transmet un pointeur vers l'objet DPC, le contenu des champs DeferredContext, SystemArgumentl et SystemArgument2 de l'objet DPC en tant que paramètres de la fonction DeferredRoutine.
Étant donné que la file d'attente DPC est servie sur l'IRQL de dispatch_level, les routines DPC sont appelées sur l'IRQL de dispatch_level. Étant donné que la file d'attente DPC est servie chaque fois que le niveau de répartition IRQL est l'IRQL de priorité la plus élevée à servir (par exemple, immédiatement après l'exécution du gestionnaire d'interruption et avant de revenir au thread utilisateur interrompu), les fonctions DPC s'exécutent dans un contexte de thread arbitraire. Par le contexte d'un thread arbitraire, nous entendons que le DPC est exécuté dans un processus et un thread, qui peuvent n'avoir rien à voir avec la demande que le DPC gère. (Le contexte d'exécution est décrit plus en détail dans la section "Modèle de pilote en couches".)
La routine DPC termine le traitement et retourne. Au retour du sous-programme DPC, le Microkernel tente d'extraire un autre objet DPC de la file d'attente DPC et de le traiter. Lorsque la file d'attente DPC est vide, le traitement DPC se termine. Le micro-noyau procède à l'appel du Dispatcher (planificateur).

Appels multiples à DPC

Chaque DPC est décrit par un objet DPC spécifique. En conséquence, chaque fois que KelnsertQueueDpc () est appelé et qu'il est déterminé que l'objet DPC qui lui est passé est déjà dans la même file d'attente DPC, KelnsertQueueDpcQ renvoie simplement (sans rien faire). Ainsi, chaque fois qu'un objet DPC est déjà dans la file d'attente DPC, toutes les tentatives ultérieures de mettre en file d'attente le même objet DPC avant que l'objet DPC ne soit supprimé de la file d'attente sont ignorées. Cela a du sens car un objet DPC ne peut être inclus physiquement que dans une file d'attente DPC à la fois.
La question évidente pourrait être: que se passe-t-il lorsqu'une demande est faite pour mettre en file d'attente un objet DPC, mais que le système exécute déjà le sous-programme DPC spécifié par cet objet DPC (sur le même processeur ou sur un processeur différent)? La réponse à cette question peut être trouvée en lisant attentivement la section précédente. Lorsque le Microkernel sert la file d'attente DPC, il supprime l'objet DPC de la tête de la file d'attente et n'appelle qu'ensuite le sous-programme DPC spécifié par l'objet DPC. Ainsi, lorsque la routine DPC est appelée, l'objet DPC a déjà été supprimé de la file d'attente DPC du processeur. Par conséquent, lorsqu'une demande est faite pour mettre en file d'attente un objet DPG et que le système se trouve dans le sous-programme DPC spécifié dans cet objet DPC, le DPC est mis en file d'attente comme d'habitude.

DPC sur les systèmes multiprocesseurs

Contrairement à ce que certaines autres sources ont avancé, et comme cela devrait être évident dans la discussion précédente, la même routine DPC peut s'exécuter sur plusieurs processeurs simultanément. Il n'y a absolument aucun blocage par le Microkernel pour empêcher cela.
Prenons le cas d'un pilote de périphérique qui a plusieurs demandes en attente en même temps. Le périphérique pilote est interrompu sur le processeur 0, le gestionnaire d'interruption de pilote s'exécute et demande au DPC de terminer la gestion des interruptions. Il s'agit du chemin d'accès standard suivi par les pilotes Windows NT. Lorsque le gestionnaire d'interruption se termine et que le système est prêt à retourner au thread utilisateur interrompu, l'IRQL du processeur O est abaissé du DIRQL auquel l'ISR a été exécuté au niveau de répartition IRQL. En conséquence, le Microkernel gère la file d'attente DPC en supprimant l'objet DPC pilote et en appelant le sous-programme DPC spécifié dans celui-ci. La routine du pilote DPC s'exécute maintenant sur le processeur 0.
Immédiatement après avoir appelé le sous-programme du pilote DPC, le périphérique génère à nouveau l'interruption. Cependant, cette fois, pour des raisons connues uniquement du matériel, l'interruption est servie sur le processeur 1. Là encore, le gestionnaire d'interruption du pilote demande le DPC. Et, à nouveau, lorsque la routine de service d'interruption se termine, le système (processeur 1) est prêt à revenir au thread utilisateur interrompu. Cela abaisse l'IRQL du processeur 1 à l'IRQL de dispatch_level, et le Microkernel sert la file d'attente DPC. Ce faisant (et toujours en cours d'exécution sur le processeur 1), le micro-noyau supprime l'objet DPC du pilote et appelle le sous-programme DPC du pilote. La routine DPC du pilote s'exécute maintenant sur le processeur 1. En supposant que la routine DPC du pilote n'a pas encore terminé l'exécution sur le processeur 0, notez que la même routine DPC s'exécute maintenant en parallèle sur les deux processeurs.
Cet exemple souligne l'importance d'utiliser le jeu correct de mécanismes de synchronisation multiprocesseur dans les pilotes. En particulier, une fonction DPC doit utiliser des verrous rotatifs pour sérialiser l'accès à toutes les structures de données auxquelles il faut accéder dans son ensemble, à condition que la conception du pilote soit telle que plusieurs appels DPC peuvent se produire simultanément.

Caractéristiques des objets DPC

Les objets DPC ont deux caractéristiques qui affectent la manière dont ils sont traités. Ces caractéristiques sont les champs Importance et Nombre.

Importance du DPC

Chaque objet DPC a une importance, qui est stockée dans le champ Importance de l'objet DPC. Les valeurs de ce champ sont répertoriées dans ntddk.h sous les noms Highlmportance, Mediumlmportance et Lowlmportance. Cette valeur d'objet DPC affecte l'emplacement dans la file d'attente DPC où l'objet DPC est placé lorsqu'il est mis en file d'attente et si l'interruption IRQL de dispatch_level se produit lorsque l'objet DPC est mis en file d'attente. La fonction KelnitializeDpc () initialise les objets DPC avec une gravité Mediumlmportance. L'importance d'un objet DPC peut être définie à l'aide de la fonction KeSetlmportanceDpc (), dont le prototype est:

VOID KeSetlmportanceDpc (IN PKDPC Dpc,
Dans KDPCIMPORTANCE Importance);

Où:
Dpc - Pointeur vers l'objet DPC dans lequel le champ Importance doit être défini;
L'importance est la valeur d'importance à installer dans l'objet DPC.
Les DPC avec une importance moyenne ou une importance faible sont placés à la fin de la file d'attente DPC. Les objets DPC avec Highlmportance sont placés au début de la file d'attente DPC.
L'importance des objets DPC affecte également si une interruption logicielle dispatch_level est générée lorsqu'un objet DPC est mis en file d'attente. Lorsqu'un objet DPC avec une importance élevée ou moyenne est mis en file d'attente sur le processeur actuel, une interruption de niveau de répartition est toujours générée. L'interruption de dispatch_level est générée pour les DPC de faible importance ou pour les DPC destinés à un processeur différent du processeur actuel, selon un algorithme de planification complexe (et non documenté).
Le tableau 11 répertorie les situations qui déclenchent la libération de la file d'attente d'objets DPC.
La plupart des pilotes de périphériques n'auront jamais besoin de définir l'importance de leurs objets DPC. Dans le cas rare où le délai entre la demande DPC et l'exécution du DPC est excessif et que le développeur du pilote est incapable de résoudre le retard d'une autre manière, vous pouvez essayer de définir le DPC d'objet sur Highlmportance. Cependant, les pilotes de périphériques sous Windows NT ne modifient généralement pas leur valeur DPC de la valeur par défaut Mediumlmportance.

Tableau 11. Situations déclenchant le vidage de la file d'attente DPC

Priorité DPC

Les DPC fonctionnent sur le même processeur que l'ISR

Les DPC sont exécutés sur un processeur différent

Faible

La taille de la file d'attente DPC dépasse le maximum, le taux de demandes DPC est inférieur au minimum ou le système est inactif

La taille de la file d'attente DPC dépasse le maximum ou le système est inactif (thread inactif en cours d'exécution)

DPC peut être limité à l'exécution sur un processeur spécifié à l'aide de la fonction KeSetTargetProcessorDpc (), dont le prototype est:

VOID KeSetTargetProcessorDpc (IN PKDPC Dpc,
IN CCHAR numéro);

Où:
Dpc - Indique l'objet DPC pour lequel le processeur cible doit être installé;
Number est le numéro de processeur de base zéro sur lequel le DPC doit s'exécuter.
Semblable à l'importance du DPC, le DPC cible n'est presque jamais défini par un pilote de périphérique. La valeur par défaut utilisée pour effectuer DPC sur le processeur actuel est presque toujours souhaitable.
Lorsqu'un processeur cible spécifique est installé pour un objet DPC, cet objet DPC sera toujours mis en file d'attente dans la file d'attente DPC du processeur spécifié. Ainsi, par exemple, même lorsque KelnsertQueueDpc () est appelé sur le processeur 0, l'objet DPC avec le processeur 1 défini comme processeur cible sera inséré dans la file d'attente DPC sur le processeur 1.

Comme indiqué précédemment dans ce chapitre, les DPC sont le plus souvent utilisés pour terminer un gestionnaire d'interruption (ISR). Afin de permettre aux pilotes de périphériques de demander plus facilement aux DPC de compléter l'ISR à partir de leurs fonctions ISR, le gestionnaire d'E / S définit un DPC spécial qui peut être utilisé à cette fin. Ce DPC s'appelle DpcForlsr.
Le gestionnaire d'E / S insère un objet DPC dans chaque objet périphérique qu'il crée. Cet objet DPC intégré est initialisé par le pilote de périphérique, généralement la première fois que le pilote est chargé, en appelant la fonction IoInitializeDpcRequest ().
IoInitializeDpcRequest () prend comme entrée un pointeur vers un objet périphérique dans lequel l'objet DPC est incorporé, un pointeur vers une fonction de pilote à appeler et une valeur de contexte à laquelle passer cette fonction. IoInitializeDpcRequest (), à son tour, appelle KelnitializeDpc () pour initialiser l'objet DPC incorporé, en passant un pointeur de fonction de pilote comme paramètre DeferredRoutine et la valeur de contexte comme paramètre DeferredContext.
Pour demander un DPC à l'ISR, le pilote appelle simplement loRequestDpc () en passant un pointeur vers un objet périphérique. IoRequestDpc (), à son tour, appelle KelnsertQueueDpc () sur l'objet DPC intégré dans l'objet Device.
Étant donné que tous les pilotes de périphérique ont des objets de périphérique et que tous les pilotes qui utilisent des interruptions utilisent également des DPC, l'utilisation du mécanisme DpcForlsr du gestionnaire d'E / S est très pratique. En fait, la plupart des pilotes de périphériques sous Windows NT n'appellent jamais directement KelnitializeDpc () ou KelnsertQueueDpc (), mais appellent à la place loInitializeDpcRequest () et IoRequestDpc ().

Les objets de gestion comprennent des objets primitifs pour les threads, les interruptions, les minuteries, la synchronisation, le profilage et deux objets spéciaux pour l'implémentation de DPC et APC. Les objets DPC (Deferred Procedure Call) sont utilisés pour réduire le temps d'exécution d'un ISR (Interrupt Service Routines) qui est déclenché par une interruption d'un périphérique. Limiter le temps consacré aux procédures ISR réduit les chances de perdre une interruption.

Le matériel système attribue la priorité matérielle aux interruptions. Le processeur associe également un niveau de priorité au travail qu'il effectue. Le processeur ne répond qu'aux interruptions qui ont une priorité plus élevée que celle qu'il utilise actuellement. Le niveau de priorité normal (y compris le niveau de priorité de l'ensemble du mode utilisateur) est 0. Les interruptions de périphérique se produisent au niveau 3 ou supérieur, et l'interruption de périphérique ISR fonctionne généralement au même niveau de priorité que l'interruption (donc d'autres interruptions moins importantes ne s'est produite lors du traitement d'une interruption plus importante).

Si l'ISR prend trop de temps, la maintenance des interruptions de priorité inférieure sera retardée, ce qui peut entraîner une perte de données ou un ralentissement des E / S du système. Plusieurs ISR peuvent être exécutés à un moment donné, chaque ISR consécutif résultant d'interruptions avec un niveau de priorité toujours plus élevé.

Pour réduire le temps de traitement ISR, seules les opérations critiques sont effectuées, telles que l'écriture des résultats d'E / S et la réinitialisation du périphérique. Le traitement ultérieur de l'interruption est reporté jusqu'à ce que le niveau de priorité du processeur soit abaissé et ne bloque plus le service des autres interruptions. L'objet DPC est utilisé pour représenter le travail à effectuer et l'ISR appelle la couche noyau pour placer le DPC dans la liste DPC spécifique au processeur. Si DPC est le premier dans la liste, alors le noyau enregistre une requête d'interruption matérielle spéciale pour le niveau de processeur 2 (auquel NT appelle le niveau DISPATCH). Lorsque le dernier ISR existant se termine, le niveau d'interruption du processeur tombe en dessous de 2, ce qui déverrouille l'interruption pour le traitement DPC. L'interruption DPC ISR traitera chacun des objets DPC (que le noyau a mis en file d'attente).

La technique d'utilisation des interruptions logicielles pour différer la gestion des interruptions est une technique établie pour réduire la latence ISR. UNIX et d'autres systèmes ont commencé à utiliser le traitement différé dans les années 1970 (pour faire face à un matériel lent et à des tampons série limités). Les ISR ont reçu des symboles de l'équipement et les ont mis en file d'attente. Une fois que tout le traitement d'interruption de haut niveau a été terminé, l'interruption logicielle a déclenché un ISR de faible priorité pour traiter les caractères (par exemple, pour implémenter le curseur d'une position en arrière - pour cela, un caractère de contrôle a été envoyé au terminal pour effacer le dernier caractère affiché et le curseur a reculé) ...

Un exemple similaire dans Windows moderne est le clavier. Après avoir appuyé sur une touche, l'ISR du clavier lit le code de la touche dans le registre, puis active à nouveau l'interruption du clavier, mais ne traite pas la touche immédiatement. Au lieu de cela, il utilise le DPC pour mettre en file d'attente le traitement du code clé (jusqu'à ce que toutes les interruptions sur le périphérique à traiter aient été traitées).

Étant donné que les DPC fonctionnent au niveau de la couche 2, ils n'interfèrent pas avec l'ISR des périphériques, mais interfèrent avec les threads de l'exécution jusqu'à ce que tous les DPC en file d'attente soient terminés et que le niveau de priorité du processeur tombe en dessous de 2. Les pilotes de périphériques et le système ne devraient pas prendre trop de temps à s'exécuter ISR ou DPC. Étant donné que les flux ne sont pas autorisés à s'exécuter, ISR et DPC peuvent ralentir le système et provoquer des plantages lors de la lecture de musique (bloquant les flux qui écrivent de la musique du tampon vers le périphérique audio). Un autre cas d'utilisation courant pour les DPC est l'exécution de routines d'interruption de minuterie. Pour éviter de bloquer les threads, les événements du minuteur (qui doivent prendre beaucoup de temps à s'exécuter) doivent mettre les requêtes en file d'attente dans le pool de threads de travail (que le noyau prend en charge pour le travail en arrière-plan).

LA CLOCHE

Il y a ceux qui ont lu cette nouvelle avant vous.
Abonnez-vous pour recevoir les derniers articles.
Email
Nom
Nom de famille
Comment voulez-vous lire The Bell
Pas de spam