Optimisation du verrouillage du noyau pour l'amélioration des performances du système d'exploitation

La performance est reine, et l'amélioration des performances du système est l'objectif de chaque ingénieur. Actuellement, l'optimisation des performances se concentre sur la suppression des inefficacités dans la pile logicielle du système ou sur le contournement des opérations système à surcharge élevée. Par exemple, le contournement du noyau atteint cet objectif en déplaçant plusieurs opérations dans l'espace utilisateur et en refactorisant le système d'exploitation sous-jacent pour certaines classes d'applications.

Dans de nombreux domaines, la spécialisation semble être la réponse à de meilleures performances, à la fois dans l'application et dans le noyau, et même entre différents sous-systèmes du noyau. En particulier, la spécialisation peut créer le contexte dans lequel une application demande certaines fonctionnalités au système. Alors que la spécialisation des applications et le noyau contournent le stockage, la mise en réseau et les accélérateurs, le contrôle de la simultanéité dans le noyau peut être critique pour les performances globales.

af6e92d3997d53bd27210b0d7a3e1f7f.jpeg

1. Les performances du système d'exploitation : verrouillage du noyau

Les verrous du noyau sont un mécanisme permettant de contrôler l'accès des processus aux ressources partagées. Dans le noyau Linux, les verrous du noyau sont implémentés en attribuant un verrou spécial au moment de la création du processus. Lorsqu'un processus a besoin d'accéder à une ressource partagée, le noyau vérifie si le processus détient déjà le verrou. Si ce n'est pas le cas, le processus sera ajouté à la file d'attente en attente du verrou et attendra que d'autres processus libèrent le verrou.

Les verrous du noyau sont essentiels pour obtenir de bonnes performances et une bonne évolutivité des applications.Cependant, les primitives de synchronisation du noyau sont généralement invisibles et hors de portée des développeurs d'applications. Concevoir des algorithmes de verrouillage et vérifier leur exactitude est déjà un défi, et l'hétérogénéité croissante du matériel le rend encore plus difficile. Le manque de conscience des développeurs de l'environnement dans lequel le verrou fonctionne, des problèmes tels que l'inversion de priorité et la priorité du détenteur du verrou, est essentiellement un manque de contexte.

Existe-t-il un moyen pour les applications de l'espace utilisateur de régler le contrôle de la concurrence dans le noyau ?

Par exemple, un utilisateur peut hiérarchiser des tâches spécifiques ou des appels système qui détiennent un ensemble de verrous. Les utilisateurs peuvent appliquer des politiques spécifiques au matériel, telles que le verrouillage asymétrique prenant en charge le multitraitement, et peuvent hiérarchiser les lectures et les écritures en fonction d'une charge de travail donnée. Il peut être possible d'améliorer encore les performances du système si les développeurs sont autorisés à régler les différents verrous du noyau, à modifier leurs paramètres et leur comportement, et même à changer entre différentes implémentations de verrous.

La spécialisation de la pile logicielle est une nouvelle façon d'améliorer les performances des applications, qui propose de pousser le code vers le cœur à des fins de performances, et d'améliorer l'évolutivité des applications en évitant le goulot d'étranglement lié à l'augmentation du nombre de cœurs. Au fil du temps, même un noyau monolithique comme Linux a commencé à permettre aux applications de l'espace utilisateur de personnaliser le comportement du noyau. Les développeurs peuvent utiliser eBPF pour personnaliser le noyau à des fins de traçage, de sécurité et même de performances.

En plus d'eBPF, les développeurs Linux utilisent également io_uring, un tampon en anneau de mémoire partagé entre l'espace utilisateur et le noyau, pour accélérer les opérations d'E/S asynchrones. De plus, les applications d'aujourd'hui peuvent gérer la pagination à la demande entièrement dans l'espace utilisateur.

Les applications contrôlent les mécanismes de concurrence du noyau sous-jacent, ce qui offre diverses opportunités aux concepteurs de verrous et aux développeurs d'applications.

30c0203fd2d8ff2a815ff370069c651b.jpeg

2. Serrures : passé, présent et futur

Le matériel est un facteur majeur dans la détermination de l'évolutivité des verrous, affectant ainsi l'évolutivité des applications. Par exemple, avec les verrous basés sur la file d'attente, le trafic excessif peut être réduit lorsque plusieurs threads acquièrent des verrous en même temps. Dans le même temps, les verrous hiérarchiques utilisent le traitement par lots pour minimiser le problème de l'écrasement des lignes de cache.

SHFLLock propose une nouvelle idée de conception d'algorithmes de verrouillage en découplant les stratégies de verrouillage et les implémentations pour réduire la surcharge de mémoire du noyau et la dégradation des performances. Introduit principalement le concept d'un programme shuffler, qui réorganise la file d'attente ou modifie l'état des threads en attente. Alors que ShflLocks fournit un moyen d'appliquer la politique, il y a aussi une tentative de se concentrer sur les politiques communes sur un simple ensemble d'API d'acquisition/libération de verrou. Pour répondre aux besoins des applications, en analysant les verrous de noyau spécifiques affectant une charge de travail donnée, les développeurs d'applications doivent définir leurs politiques de manière contrôlée et sûre, et mettre à jour dynamiquement les politiques d'acquisition de verrous, en utilisant le shuffler pour appliquer les politiques.

3 Scénario typique : planifier des threads en attente de verrous

Les threads en attente d'un verrou peuvent être planifiés de deux manières différentes : la planification en fonction de l'acquisition en fonction de l'ordre dans lequel les verrous sont acquis et la planification en fonction de l'occupation en fonction du temps passé par le thread dans la section critique.

3.1 Ordonnancement de la perception d'acquisition

Commutateurs de verrouillage, permettant aux développeurs de basculer entre différents algorithmes de verrouillage. Trois situations se distinguent :

  1. Passez d'une conception de verrouillage de lecteur-écriture neutre à une conception de lecteur par processeur ou basée sur NUMA pour les charges de travail intensives en lecture. Par exemple, les défauts de page et l'énumération des fichiers dans un répertoire. L'autre cas concerne un commutateur de verrouillage en lecture-écriture neutre. aux verrous en écriture pure ; un exemple est la création de plusieurs fichiers dans un répertoire.

  2. Passez d'une conception de verrou basée sur Numa à une approche combinée avec prise en compte de Numa, où le détenteur du verrou effectue des opérations pour le compte des threads en attente. Cette approche offre de meilleures performances car elle supprime au moins un transfert en cache.

  3. Basculez entre les verrous bloquants et non bloquants, et vice versa, par exemple, en désactivant la stratégie d'arrêt/réveil de la fonction de mélange de SHFLLock, en commutant les lectures bloquantes vers des verrous en lecture-écriture non bloquants (rwlock).

Cette approche présente deux avantages : premièrement, les développeurs peuvent supprimer la synchronisation temporaire, comme l'utilisation de verrous non bloquants et l'utilisation d'événements d'attente pour implémenter des stratégies d'arrêt/réveil, qui sont couramment utilisées dans les systèmes de fichiers Btrfs. Deuxièmement, il permet aux développeurs d'unifier la conception des verrous en multiplexant dynamiquement plusieurs politiques.

e6c71a7665fc272339fd56b12058c7d4.png

3.1.1 Héritage des verrous

Un processus peut acquérir plusieurs verrous pour effectuer une opération. Par exemple, un processus sous Linux peut acquérir jusqu'à 12 verrous (par exemple, une opération de renommage), ou une moyenne de 4 verrous pour effectuer des opérations de gestion de la mémoire ou des métadonnées de fichier.

Malheureusement, ce mode de verrouillage pose le problème du verrouillage basé sur la file d'attente, où certains threads doivent attendre plus longtemps pour acquérir le verrou de niveau supérieur, qui est effectué par un autre thread en attente d'un autre verrou. Par exemple, supposons que le thread t1 souhaite acquérir deux verrous, L1, puis L2 en une seule opération, et que t2 souhaite uniquement récupérer l'opération sur L1. Étant donné que ces protocoles de verrouillage sont basés sur fifo, t1 peut obtenir L2 à la fin de la file d'attente pendant que t2 attend d'obtenir L1. Le développeur peut fournir plus de contexte au noyau : soit t1 acquiert tous les verrous ensemble, soit t1 déclare les verrous qu'il détient déjà, ce qui peut lui donner une priorité plus élevée pour acquérir le prochain verrou L2.

Une application peut souhaiter hiérarchiser un chemin d'appel système ou un ensemble de tâches pour de meilleures performances. Les développeurs peuvent le faire en codant le contexte de priorité des tâches et en transmettant ces informations aux verrous concernés. Pour les appels système, les développeurs peuvent partager des informations sur un ensemble de verrous et de threads prioritaires sur le chemin critique. Le programme shuffler accordera alors la priorité à ces threads par rapport aux autres threads attendant le verrou de l'application spécifiée.

3.1.2 Exposer la sémantique du planificateur

En général, le surabonnement aux ressources matérielles, telles que le processeur ou la mémoire, entraîne une meilleure utilisation des ressources, à la fois pour les systèmes d'exécution de l'espace utilisateur et les machines virtuelles. Alors que le surabonnement améliore l'utilisation du matériel, il introduit également un problème de double planification. L'hyperviseur peut programmer un vCPU pour qu'il serve de détenteur de verrou ou de verrou suivant dans la machine virtuelle. L'hyperviseur peut exposer les informations de planification de vCPU au programme shuffler pour hiérarchiser les services en fonction de leurs quotas d'exécution.

3.1.3 Stratégie arrêt/réveil adaptative

Toutes les écluses fermées suivent la stratégie de stationnement après rotation, c'est-à-dire qu'elles s'arrêtent d'elles-mêmes après avoir tourné pendant un certain temps. Ce temps de rotation est principalement ad hoc, c'est-à-dire que le serveur tourne pendant un certain temps en fonction du quota de temps ou continue de tourner s'il n'y a aucune tâche à effectuer. Les développeurs d'applications peuvent désormais exposer le contexte temporel après avoir analysé la longueur des sections critiques afin de minimiser la consommation d'énergie et les informations de réveil pour programmer le prochain serveur à l'heure afin de minimiser la latence de réveil. De plus, les développeurs peuvent encoder davantage les informations de veille pour réveiller les serveurs avant le verrouillage afin de réduire les longs délais de réveil. Cette approche fonctionne également avec des spinlocks paravirtualisés pour éviter les effets de convoi.

3.2 Calendrier d'occupation

3.2.1 Héritage prioritaire

L'inversion de priorité se produit lorsqu'une tâche de faible priorité détenant un verrou est planifiée par une tâche de priorité normale pour attendre le même verrou. Le problème est indiqué dans la pile d'E/S Linux : lors de la planification d'une demande d'E/S, une tâche normale qui souhaite acquérir un verrou peut planifier une tâche d'arrière-plan de priorité inférieure qui détient le même verrou. La planification des verrous est une tâche en arrière-plan, ce qui entraîne une diminution des performances d'E/S.

3.2.2 Planification coopérative pour l'équité des tâches

Cela introduit une nouvelle classe de problèmes connue sous le nom de problème de subversion du planificateur, où deux tâches acquièrent des verrous à des moments différents. Les tâches maintenues pendant une longue période subvertissent les objectifs de planification du système d'exploitation. Le système d'exploitation résout ce problème en gardant une trace des tailles de régions critiques et en pénalisant les tâches de longue durée. Bien que cette solution résolve le problème, elle applique l'équité de planification même pour les applications qui peuvent ne pas en bénéficier.

3.2.3 Verrouillage équitable des tâches sur les machines multiprocesseurs asymétriques (AMP)

Avec des cœurs de puissance de calcul différente dans un processeur, les primitives de verrouillage de base utilisées sur cette architecture souffrent d'un problème de subversion du planificateur où le débit de l'application peut s'effondrer en raison de la puissance de calcul plus lente du cœur le plus faible. Pour accélérer les processus, les développeurs peuvent soit allouer des verrous critiques sur des cœurs plus rapides, soit réorganiser la file d'attente des threads en attente d'acquisition de verrous pour améliorer le verrouillage global.

3.2.4 Ordonnancement en temps réel

Semblable à la planification dans les systèmes en temps réel, les développeurs d'applications peuvent créer des politiques de verrouillage qui planifient toujours les threads pour garantir les SLO. Ici, le verrou peut être conçu comme un algorithme basé sur l'équité de phase.Cette approche permet également d'éliminer la gigue et garantit une limite supérieure sur la latence de queue pour les applications critiques en termes de latence.

3.3 Analyse du verrouillage dynamique

Les développeurs d'applications peuvent configurer les informations de fichier sur tous les verrous du noyau. La sélection des verrous à configurer permet aux développeurs de configurer à différents niveaux de granularité. Par exemple, ils peuvent configurer tous les spinlocks exécutés dans le noyau, verrouiller des fonctions spécifiques, des chemins de code ou des espaces de noms, ou même des instances de verrou individuelles. Cette approche profite aux développeurs d'applications en leur permettant de mieux comprendre la synchronisation sous-jacente en analysant uniquement les parties qui les intéressent.

Les développeurs peuvent également raisonner sur les contrats de performance qui affectent les performances des applications en fonction de certaines garanties fournies par diverses politiques de brassage ou même des ensembles de politiques.

4. Un cadre d'optimisation pour les verrous du noyau

Redéfinissez les décisions et les comportements utilisés par les verrous du noyau et exposez-les sous forme d'API. Le code défini par l'utilisateur remplace ces API exposées et les utilisateurs peuvent personnaliser la fonction de verrouillage en fonction de leurs besoins. Par exemple, s'il faut tourner avant de rejoindre la file d'attente pourrait être une API afin que l'utilisateur puisse prendre cette décision. L'utilisateur écrit d'abord son propre code pour modifier le protocole de verrouillage dans le noyau en fonction du cas d'utilisation, puis le système d'exploitation remplace la fonction de verrouillage annotée à l'intérieur du noyau.L'organigramme est le suivant :

2fd18abff7b3d53a0c78a0c8f97412c0.jpeg

L'utilisateur spécifie une politique de verrouillage (1), et le vérificateur eBPF la vérifie après compilation, en tenant compte des restrictions eBPF et des propriétés de sécurité d'exclusion mutuelle (2, 3). Le vérificateur informera ensuite l'utilisateur du résultat de la vérification (4) et, en cas de succès, stockera le code eBPF compilé dans le système de fichiers (5). Enfin, remplacez la fonction annotée spécifiant le verrou (6) par le module patch de champ.

4.1 API

Diverses API prennent en charge l'implémentation flexible des politiques de verrouillage tout en garantissant la sécurité. L'implémentation sous-jacente du système d'exploitation repose sur eBPF pour modifier le verrouillage du noyau. En utilisant eBPF et l'API de verrouillage, la stratégie souhaitée est implémentée pour un ensemble d'instances de verrouillage dans le noyau. Un utilisateur peut encoder plusieurs politiques, qui sont converties en code natif et dont la sécurité est vérifiée par les vérificateurs eBPF. Les vérificateurs effectuent une exécution symbolique avant de charger le code natif dans le noyau, comme le contrôle d'accès à la mémoire ou uniquement les fonctions d'assistance en liste blanche.

a3dc3db3218f5db4a677093b2e205839.jpeg

4.2 Sécurité

En plus des validateurs eBPF, ShflLocks a une phase distincte pour l'acquisition de verrous et une phase pour réorganiser la file d'attente. L'utilisateur s'appuie sur la fonction API pour comparer le nœud actuel et le nœud mélangeur et s'il faut réorganiser le nœud actuel, et peut également concevoir un verrou coopératif de planificateur pour hiérarchiser les nœuds avec une longueur de tranche critique plus petite, réduisant ainsi la priorité du nœud classe.

Bien que des implémentations utilisateur incorrectes puissent enfreindre la politique d'équité garantie, la propriété mutex peut être vérifiée et garantie lors de l'exécution. De plus, le noyau n'a pas de problèmes de blocage, l'API ne modifie pas le comportement du verrouillage, elle renvoie simplement la décision de déplacer le nœud. Les développeurs peuvent configurer leurs verrous de manière précise en implémentant le comportement souhaité pour chaque appel. Sans modifier le comportement de la fonction de verrouillage, la stratégie d'analyse du profil de poids peut augmenter la longueur de la section critique, entraînant une dégradation des performances.

De plus, eBPF offre la possibilité de chaîner plusieurs programmes eBPF que les utilisateurs peuvent utiliser pour écrire des stratégies. Enfin, nous nous appuyons également sur les structures de données de répartition des champs pour modifier les structures de données utilisées par les primitives de verrouillage. Par exemple, la structure de données de nœud de verrouillage basée sur une file d'attente peut être étendue avec des informations supplémentaires utilisées pour coder des informations pour des cas d'utilisation spécifiques. Dans le pire des cas, sans exécuter le code de l'espace utilisateur, la modification dynamique de l'algorithme de verrouillage peut entraîner jusqu'à 20 % de surcharge.

4.3 Stratégie de combinaison

En ajustant les contrôles de concurrence du noyau, les applications peuvent avoir plus de contrôle sur la pile logicielle. Les développeurs d'applications fournissent un ensemble de stratégies qui nécessitent un verrou d'application. Combiner plusieurs stratégies est une tâche difficile, surtout lorsque certaines stratégies peuvent entrer en conflit. En tirant parti de la composition du programme pour automatiser ce processus, il peut être possible de déplacer entièrement les propriétés de sécurité vers la vérification dans l'espace utilisateur, et également de fournir un moyen sûr de composer des politiques conflictuelles.

Les utilisateurs ne peuvent pas ajouter trop de stratégies car leur exécution peut tomber sur le chemin critique. Permet à un utilisateur privilégié de modifier le verrouillage du noyau, le modèle est uniquement pour un utilisateur utilisant l'ensemble du système. Cependant, pour gérer l'hébergement multiclient dans un environnement cloud, un rédacteur de stratégie tenant compte du locataire qui ne viole pas l'isolement entre les utilisateurs est requis. Synthétisez les politiques dans l'espace utilisateur pour éviter de tels conflits et ajoutez des contrôles d'exécution aux algorithmes de verrouillage qui ne sont utilisés que lorsque les politiques peuvent affecter certains comportements.

Outre les verrous, il existe d'autres mécanismes de synchronisation largement utilisés dans le noyau, tels que RCU, seqlocks, événements d'attente et autres extensions qui permettront aux applications d'améliorer leurs performances. Cela dit, les applications de l'espace utilisateur ont également leurs propres verrous qui sont de nature générique. En revanche, les techniques existantes, telles que l'interpolation de bibliothèque, n'autorisent qu'un seul changement pour différentes implémentations de verrous lorsque l'application commence à s'exécuter.

5. Résumé

Les primitives de synchronisation verrouillées par le noyau ont un impact énorme sur les performances et l'évolutivité de certaines applications, cependant, le contrôle des primitives de synchronisation du noyau est hors de portée des développeurs d'applications. Si le contrôle contextuel de la concurrence est utilisé, il permet aux applications de l'espace utilisateur d'affiner les primitives de concurrence du noyau. C'est une façon de penser la spécialisation de la pile logicielle, qui accélère en partie l'innovation dans le domaine de la synchronisation du cœur.

[Documents de référence et lectures connexes]

Je suppose que tu aimes

Origine blog.csdn.net/wireless_com/article/details/131160180
conseillé
Classement