Noyau Windows unique

Windows est l'un des systèmes d'exploitation les plus polyvalents et flexibles, il fonctionne sur des architectures complètement différentes et est disponible en différentes versions. Aujourd'hui, il prend en charge les architectures x86, x64, ARM et ARM64. Windows a pris en charge Itanium, PowerPC, DEC Alpha et MIPS. En outre, Windows prend en charge une large gamme de SKU fonctionnant dans diverses conditions; Des centres de données, ordinateurs portables, Xbox et téléphones aux versions intégrées de l'Internet des objets, par exemple, dans les distributeurs automatiques de billets.

L'aspect le plus étonnant est que le noyau Windows reste pratiquement inchangé en fonction de toutes ces architectures et SKU . Le noyau évolue dynamiquement en fonction de l'architecture et du processeur sur lesquels il fonctionne, afin de profiter pleinement de l'équipement. Bien sûr, le noyau a une certaine quantité de code associée à une architecture spécifique, mais il y en a une quantité minimale, ce qui permet à Windows de fonctionner sur une variété d'architectures.

Dans cet article, je parlerai de l'évolution des éléments clés du noyau Windows qui lui permettent d'évoluer de manière transparente de la puce NVidia Tegra basse consommation fonctionnant sur Surface RT 2012 aux monstres géants travaillant dans les centres de données Azure.


Un gestionnaire de tâches Windows s'exécutant sur une machine préliminaire Windows DataCenter, avec 896 cœurs prenant en charge 1792 processeurs logiques et 2 To de mémoire

Évolution du noyau unique


Avant de discuter des détails du noyau Windows, penchons-nous un peu vers le refactoring . Le refactoring joue un rôle clé dans l'augmentation de la réutilisation des composants du système d'exploitation sur diverses SKU et plates-formes (par exemple, client, serveur et téléphone). L'idée de base du refactoring est de vous permettre de réutiliser la même DLL sur différentes références, en prenant en charge de petites modifications apportées spécifiquement pour la référence souhaitée, sans renommer la DLL et sans interrompre le travail des applications.

La technologie de base du refactoring Windows est une technologie peu documentée appelée ensembles d'API . Les suites d'API sont un mécanisme qui permet au système d'exploitation de séparer la DLL et son lieu d'utilisation. Par exemple, le jeu d'API permet aux applications pour win32 de continuer à utiliser kernel32.dll, malgré le fait que l'implémentation de toutes les API est écrite dans une autre DLL. Ces DLL d'implémentation peuvent également différer entre les SKU. Vous pouvez voir les ensembles d'API en action en exécutant la traversée des dépendances sur une DLL Windows traditionnelle, par exemple, kernel32.dll.


Après avoir terminé cette digression sur la structure de Windows, qui permet au système de maximiser la réutilisation et le partage de code, passons aux profondeurs techniques du démarrage du noyau selon le planificateur, qui est la clé pour faire évoluer le système d'exploitation.

Composants du noyau


Windows NT est, en fait, un micro - noyau , dans le sens où il possède son propre noyau (KE) avec un ensemble limité de fonctions, utilisant la couche exécutable (couche exécutive, Ex) pour exécuter toutes les stratégies de haut niveau. EX est toujours en mode noyau, donc ce n'est pas exactement un micro-noyau. Le noyau est responsable de la planification des threads, de la synchronisation entre les processeurs, de la gestion des exceptions au niveau matériel et de l'implémentation de fonctions dépendantes du matériel de bas niveau. La couche EX contient divers sous-systèmes qui fournissent un ensemble de fonctionnalités qui est généralement considéré comme le noyau - IO, gestionnaire d'objets, gestionnaire de mémoire, sous-système de processus, etc.


Pour mieux comprendre la taille des composants, voici une répartition approximative du nombre de lignes de code dans plusieurs répertoires clés de l'arborescence des sources du noyau (y compris les commentaires). Le tableau n'a pas encore inclus beaucoup de tout ce qui concerne le noyau.

Sous-systèmes du noyauLignes de code
Gestionnaire de mémoire501 000
Registre211 000
Puissance238 000
Exécutif157 000
La sécurité135 000
Noyau339 000
Sous-système de processus116 000

Pour plus d'informations sur l'architecture Windows, consultez la série de livres Windows Internals .

Planificateur


Après avoir préparé le terrain de cette façon, parlons un peu du planificateur, de son évolution et de la façon dont le noyau Windows peut évoluer vers un tel nombre d'architectures différentes avec autant de processeurs.

Un thread est une unité de base qui exécute du code de programme, et c'est précisément son travail que le planificateur Windows planifie. Pour décider quel thread démarrer, le planificateur utilise leurs priorités et, en théorie, le thread avec la priorité la plus élevée devrait démarrer sur le système, même si cela signifie qu'il ne restera plus de temps pour les threads avec des priorités plus faibles.

Ayant travaillé le temps quantique (le temps minimum qu'un thread peut travailler), le thread subit une diminution de la priorité dynamique afin que les threads de haute priorité ne puissent pas fonctionner indéfiniment, l'âme de tout le monde. Lorsqu'un autre thread se réveille pour fonctionner, il reçoit la priorité, calculée en fonction de l'importance de l'événement qui a causé l'attente (par exemple, la priorité est considérablement augmentée pour l'interface utilisateur frontale, et pas beaucoup - pour terminer les opérations d'E / S). Par conséquent, un thread fonctionne avec une priorité élevée tout en restant interactif. Quand il devient principalement connecté aux calculs (liés au CPU), sa priorité diminue et ils y reviennent après que d'autres threads avec une priorité élevée ont leur temps de processeur. De plus, le noyau augmente arbitrairement la priorité des threads prêts à l'emploi qui n'ont pas reçu de temps processeur pendant une certaine période afin d'éviter leur famine de calcul et de corriger l'inversion de priorité.

Le planificateur Windows avait initialement une file d'attente de préparation, à partir de laquelle il a sélectionné le thread suivant, la plus prioritaire à exécuter. Cependant, avec le début de la prise en charge d'un nombre croissant de processeurs, la seule file d'attente s'est transformée en goulot d'étranglement et le planificateur a modifié le travail autour de la zone de publication de Windows Server 2003 et organisé une file d'attente de disponibilité par processeur. Lors du passage à la prise en charge de plusieurs demandes pour un processeur, ils n'ont pas effectué un seul verrouillage global protégeant toutes les files d'attente et ont permis au planificateur de prendre des décisions en fonction des optima locaux. Cela signifie qu'à tout moment dans le système, il existe un thread avec la priorité la plus élevée, mais cela ne signifie pas nécessairement que N des threads de la priorité la plus élevée (où N est le nombre de processeurs) fonctionne dans le système. Cette approche a porté ses fruits jusqu'à ce que Windows commence à passer à des processeurs à faible consommation tels que les ordinateurs portables et les tablettes. Lorsque le thread avec les priorités les plus élevées ne fonctionnait pas sur de tels systèmes (par exemple, le thread frontal de l'interface utilisateur), cela entraînait des problèmes d'interface perceptibles. Par conséquent, dans Windows 8.1, le planificateur a été transféré vers un modèle hybride, avec des files d'attente pour chaque processeur pour les threads associés à ce processeur, et une file d'attente partagée de processus prêts à l'emploi pour tous les processeurs. Cela n'a pas affecté les performances de manière notable en raison d'autres modifications de l'architecture du planificateur, par exemple, la refactorisation d'un verrou de base de données de répartiteur.

Windows 7 a introduit une telle chose comme un planificateur de partage équitable dynamique (Dynamic Fair Share Scheduler, DFSS); cela concernait principalement les serveurs de terminaux. Cette fonctionnalité a essayé de résoudre le problème qu'une session de terminal avec une charge CPU élevée pouvait affecter les threads dans d'autres sessions de terminal. Étant donné que le planificateur ne prenait pas en compte les sessions et utilisait simplement la priorité pour distribuer les flux, les utilisateurs des différentes sessions pouvaient influencer le travail des utilisateurs des autres sessions en étranglant leurs flux. Cela donnait également un avantage injuste aux sessions (et aux utilisateurs) avec un grand nombre de threads, car une session avec un grand nombre de threads avait plus d'occasions d'obtenir du temps processeur. Une tentative a été faite pour ajouter une règle au planificateur, selon laquelle chaque session était considérée sur un pied d'égalité avec les autres en termes de temps processeur. Des fonctionnalités similaires existent sous Linux avec leur ordonnanceur complètement honnête ( Completely Fair Scheduler ). Dans Windows 8, ce concept a été généralisé en tant que groupe de planificateurs et ajouté au planificateur, à la suite de quoi chaque session est tombée dans un groupe indépendant. En plus des priorités pour les threads, le planificateur utilise les groupes du planificateur comme index de deuxième niveau, en décidant quel thread commencer ensuite. Dans le serveur Terminal Server, tous les groupes de planificateurs ont le même poids, de sorte que toutes les sessions reçoivent la même quantité de temps processeur, quel que soit le nombre ou la priorité des threads au sein des groupes de planificateurs. De plus, ces groupes sont également utilisés pour un contrôle plus précis des processus. Dans Windows 8, les objets Job ont été améliorés pour prendre en charge la gestion du temps du processeur . À l'aide d'une API spéciale, vous pouvez décider du temps processeur qu'un processus peut utiliser, qu'il s'agisse d'une limite souple ou stricte, et recevoir des notifications lorsque le processus atteint ces limites. Ceci est similaire à la gestion des ressources dans les cgroups sous Linux.

À partir de Windows 7, Windows Server a introduit la prise en charge de plus de 64 processeurs logiques sur un seul ordinateur. Pour ajouter un support à autant de processeurs, une nouvelle catégorie a été introduite dans le système, le «groupe de processeurs». Un groupe est un ensemble invariable de processeurs logiques ne dépassant pas 64 éléments, qui sont considérés par un ordonnanceur comme une unité informatique. Le noyau au démarrage détermine quel processeur appartient à quel groupe, et pour les machines avec moins de 64 cœurs de processeur, cette approche est presque impossible à remarquer. Un processus peut être divisé en plusieurs groupes (par exemple, une instance de SQL Server), un seul thread à la fois ne peut être exécuté que dans le même groupe.

Mais sur les machines où le nombre de cœurs de processeur dépasse 64, Windows a commencé à montrer de nouveaux goulots d'étranglement qui empêchaient les applications exigeantes telles que le serveur SQL d'être mises à l'échelle linéairement avec le nombre croissant de cœurs de processeur. Par conséquent, même avec l'ajout de nouveaux cœurs et de mémoire, les mesures de vitesse n'ont pas montré d'augmentation significative. L'un des principaux problèmes associés à cette situation était le différend concernant le blocage de la base des répartiteurs. Le verrouillage de la base de données du répartiteur protégeait l'accès aux objets dont le travail devait être planifié. Parmi ces objets figurent des threads, des temporisateurs, des ports d'entrée / sortie, d'autres objets du noyau qui sont sujets à attente (événements, sémaphores, mutex). Sous la pression de la nécessité de résoudre ces problèmes, un travail a été effectué dans Windows 7 pour éliminer le blocage de la base de données du répartiteur et le remplacer par des ajustements plus précis, tels que le verrouillage objet par bloc. Cela a permis à des mesures de performances telles que SQL TPC-C de démontrer une augmentation de 290% de la vitesse par rapport au schéma précédent sur certaines configurations. Il s'agit de l'une des plus grandes augmentations de performances de l'histoire de Windows qui s'est produite en raison d'un changement dans une seule fonctionnalité.

Windows 10 a apporté une autre innovation en introduisant des ensembles de processeurs. Les ensembles d'UC permettent à un processus de partitionner un système afin qu'un processus puisse être réparti sur plusieurs groupes de processeurs, empêchant d'autres processus de les utiliser. Le noyau Windows ne permet même pas aux interruptions de périphérique d'utiliser les processeurs inclus dans votre ensemble. Cela garantit que même les appareils ne peuvent pas exécuter leur code sur les processeurs émis pour le groupe de votre application. Cela ressemble à une machine virtuelle low-tech. Il est clair que c'est une fonctionnalité puissante, de nombreuses mesures de sécurité y sont intégrées afin que le développeur de l'application ne fasse pas de grosses erreurs lorsqu'il travaille avec l'API. La fonctionnalité des jeux de CPU est utilisée en mode jeu.

Enfin, nous arrivons à la prise en charge d'ARM64, qui est apparu dans Windows 10 . L'architecture ARM prend en charge l'architecture big.LITTLE , qui est de nature hétérogène - le «grand» cœur est rapide et consomme beaucoup d'énergie, et le «petit» cœur est lent et consomme moins. L'idée est que des tâches insignifiantes peuvent être effectuées sur un petit noyau, économisant ainsi la batterie. Pour prendre en charge l'architecture big.LITTLE et augmenter la durée de vie de la batterie lors de l'exécution de Windows 10 sur ARM, une prise en charge de mise en page hétérogène a été ajoutée au planificateur, en tenant compte des souhaits d'une application travaillant avec l'architecture big.LITTLE.

Par souhaits, je veux dire que Windows essaie de fournir un service de qualité pour les applications, de suivre les threads qui s'exécutent au premier plan (ou ceux qui manquent de temps processeur), et de garantir leur exécution sur le "gros" cœur. Toutes les tâches, services et autres threads auxiliaires en arrière-plan s'exécutent sur de petits cœurs. Dans le programme, vous pouvez également noter de force la faible importance du thread afin de le faire fonctionner sur le petit noyau.

Travailler pour le compte de quelqu'un d'autre [Travailler pour le compte]: dans Windows, beaucoup de travail au premier plan est effectué par d'autres services qui travaillent en arrière-plan. Par exemple, lors d'une recherche dans Outlook, la recherche elle-même est effectuée par le service d'arrière-plan Indexer. Si nous exécutons tous les services sur un petit noyau, la qualité et la vitesse des applications au premier plan en souffriront. Pour l'empêcher de ralentir sur les architectures big.LITTLE dans de tels scénarios de travail, Windows surveille les appels d'application arrivant vers d'autres processus afin d'effectuer des travaux en leur nom. Dans ce cas, nous donnons la priorité au premier plan au thread lié au service et le forçons à s'exécuter sur le gros noyau.

Permettez-moi de terminer ce premier article sur le noyau Windows, qui donne un aperçu du planificateur. Des articles avec des détails techniques similaires sur le fonctionnement interne de l'OS suivront plus tard.

Source: https://habr.com/ru/post/fr428469/


All Articles