Le noyau Linux est un système complexe qui doit gérer divers problèmes de concurrence, tels que la planification des processus, la gestion de la mémoire, les pilotes de périphériques, les protocoles réseau, etc. Afin de garantir la cohérence et l'exactitude des données, le noyau Linux fournit une variété de mécanismes de synchronisation, tels que des verrous tournants, des sémaphores, des verrous en lecture-écriture, etc. Cependant, ces mécanismes de synchronisation présentent quelques défauts, tels que :
Alors, existe-t-il un meilleur mécanisme de synchronisation ? La réponse est oui, c'est RCU (Read Copy Update). RCU est un mécanisme de synchronisation basé sur le modèle de publication-abonnement, qui peut réaliser des opérations de lecture efficaces et des opérations de mise à jour à faible latence. L'idée de base de RCU est la suivante :
Quels sont les avantages du RCU ? Il présente les fonctionnalités suivantes :
Le signe que le côté lecture RCU entre dans la section critique est d'appeler rcu_read_lock. Le code de cette fonction est :
.1. 2. static inline void rcu_read_lock(void) 3. { 4. __rcu_read_lock(); 5. __acquire(RCU); 6. rcu_read_acquire(); 7. }
Il semble y avoir trois appels de fonction dans cette implémentation, mais le travail réel est effectué par la première fonction __rcu_read_lock(). __rcu_read_lock() désactive la préemption du noyau en appelant preempt_disable(). Mais les interruptions sont autorisées. Supposons que le lecteur se trouve dans la section critique rcu et qu'il vient de lire un pointeur p dans la zone de données partagées (mais n'a pas encore accédé aux données membres de p). Une interruption se produit et l'exemple de gestion des interruptions est activé. Il se trouve que l'ISR doit modifier la zone de données pointée par p. Selon les principes de conception de RCU, l'ISR allouera une nouvelle zone de données new_p de la même taille, puis copiera les données de l'ancienne zone de données p dans la nouvelle. zone de données, puis dans new_p Effectuez essentiellement le travail de modification des données (car elles sont modifiées dans l'espace new_p, il n'y a pas d'accès simultané à p, donc RCU est un mécanisme sans verrouillage, et c'est la raison), après l'ISR termine le travail de mise à jour des données, attribue new_p à p (p=new_p), et enfin il enregistrera une fonction de rappel pour libérer l'ancien pointeur p au moment approprié. Par conséquent, tant que toutes les références à l’ancien pointeur p sont terminées, il n’y a aucun problème à libérer p. Lorsque la routine de traitement des interruptions revient après avoir terminé ces tâches, le processus interrompu accédera toujours aux données dans l'espace p, c'est-à-dire aux anciennes données. Ce résultat est autorisé par le mécanisme RCU.
Les règles RCU autorisent des incohérences temporaires dans l'affichage des ressources causées par le basculement du pointeur entre les lecteurs et les rédacteurs. La prochaine question intéressante à propos de RCU est : quand l'ancien pointeur peut-il être libéré. La réponse à cette question que j'ai vue dans de nombreux livres est la suivante : lorsqu'un changement de processus se produit sur tous les processeurs du système. Cette réponse stylisée déroute souvent les lecteurs qui découvrent le mécanisme RCU. Pourquoi devons-nous attendre qu'un changement de processus se produise sur tous les processeurs avant d'appeler la fonction de rappel pour libérer l'ancien pointeur ? Ceci est en fait déterminé par les règles de conception de RCU : Toutes les références aux anciens pointeurs ne peuvent apparaître que dans la section critique incluse dans rcu_read_lock et rcu_read_unlock, et la commutation de processus n'est pas possible dans cette section critique , Une fois la section critique ne devrait plus n'ont plus aucune forme de référence à l'ancien pointeur p. Évidemment, cette règle exige que le lecteur ne puisse pas changer de processus dans la section critique, car une fois qu'il y a un changement de processus, la fonction de rappel qui libère l'ancien pointeur peut être appelée, provoquant la libération de l'ancien pointeur lors du changement de processus. est replanifié, il peut faire référence à un espace mémoire libéré. Nous voyons maintenant pourquoi rcu_read_lock doit uniquement désactiver la préemption du noyau, car cela rend impossible la commutation et la suppression du processus actuel même si une interruption se produit dans la section critique. Les développeurs de noyau, ou plutôt les concepteurs de RCU, ne peuvent pas faire grand-chose. L'étape suivante relève de la responsabilité de l'utilisateur. Si une fonction est appelée dans la section critique de RCU, la fonction peut se mettre en veille, alors les règles de conception de RCU seront violées et le système entrera dans un état instable. Cela montre une fois de plus que si vous voulez utiliser quelque chose, vous devez comprendre son mécanisme interne. Comme dans l'exemple mentionné ci-dessus, même s'il n'y a actuellement aucun problème avec le programme, les dangers cachés laissés dans le système sont comme une bombe à retardement. , ce qui peut arriver à tout moment. Une détonation, surtout s'il faut beaucoup de temps avant que le problème n'éclate soudainement. Dans la plupart des cas, le temps nécessaire pour trouver le problème peut être beaucoup plus long que pour se calmer et comprendre soigneusement les principes de la RCU. Les lecteurs de RCU ont un degré de liberté plus élevé que les lecteurs de rwlock. Étant donné que le lecteur RCU n'a pas besoin de prendre en compte les sentiments de l'écrivain lorsqu'il accède à une ressource partagée, cela est différent du lecteur rwlock. Le lecteur rwlock doit s'assurer qu'aucun écrivain n'exploite la ressource lors de la lecture de la ressource partagée. La différence entre les deux vient du fait que RCU sépare les ressources partagées entre lecteurs et écrivains, tandis que les lecteurs et écrivains rwlock n'utilisent qu'une seule copie des ressources partagées du début à la fin. Cela signifie également que les rédacteurs de RCU doivent assumer davantage de responsabilités et qu'une sorte de mécanisme d'exclusion mutuelle doit être introduit entre plusieurs rédacteurs qui mettent à jour la même ressource partagée. RCU est donc un « mécanisme sans verrouillage ». La déclaration est limitée aux lecteurs et écrivains. Nous voyons donc que le mécanisme RCU doit être utilisé dans des situations où il y a un grand nombre d'opérations de lecture et relativement peu d'opérations de mise à jour. À l'heure actuelle, RCU peut grandement améliorer les performances du système, car l'opération de lecture de RCU n'entraîne pratiquement aucune surcharge de verrouillage par rapport à certains autres mécanismes de verrouillage.
L'implémentation de synchroniser_rcu utilise la file d'attente. Lors de son implémentation, elle ajoute également un nœud à la liste chaînée locale du processeur actuel comme call_rcu. La différence avec call_rcu est que la fonction de rappel dans ce nœud est wakeme_after_rcu, puis synchronise_rcu. dormira dans une file d'attente jusqu'à ce qu'un changement de processus se produise sur tous les processeurs du système, donc wakeme_after_rcu est appelé par rcu_process_callbacks pour réveiller le synchronisme_rcu endormi. Après avoir été réveillé, synchroniser_rcu sait qu'il peut maintenant libérer l'ancien pointeur. Cet article présente RCU, un mécanisme de synchronisation efficace dans le noyau Linux. Il s'agit d'un mécanisme de synchronisation basé sur le modèle de publication-abonnement. Nous avons analysé les idées de base, les interfaces clés et les détails de mise en œuvre de RCU sous l'aspect principal, et avons donné des exemples de code correspondants. Nous avons également introduit l'utilisation de RCU dans les opérations de liste chaînée, la gestion des minuteries, le traitement des interruptions logicielles et d'autres scénarios du point de vue de l'application, et avons donné des exemples de code correspondants. En étudiant cet article, nous pouvons maîtriser l'utilisation de base de RCU et être en mesure d'utiliser RCU de manière flexible dans le développement réel pour répondre aux exigences de synchronisation efficaces. J'espère que cet article vous aidera !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!