J'aimerais connaître des spinlocks Linux en détail; Est-ce que quelqu'un pourrait me les expliquer?
Une serrure de spin est un moyen de protéger une ressource partagée d'être modifiée par deux processus ou plus simultanément. Le premier processus qui tente de modifier la ressource "acquiert" la serrure et continue de sa manière, de faire ce dont il a besoin avec la ressource. Tout autre processus qui tente ultérieurement d'acquérir la serrure est arrêtée; On leur est dit de "tourner en place" en attente de la serrure à libérer par le premier processus, donc le nom de spin.
Le noyau Linux utilise des serrures de spin pour de nombreuses choses, telles que lors de l'envoi de données à un périphérique particulier. La plupart des périphériques matériels ne sont pas conçus pour gérer plusieurs mises à jour simultanées de l'état. Si deux modifications différentes doivent se produire, il faut suivre strictement l'autre, ils ne peuvent pas se chevaucher. Une serrure de spin offre la protection nécessaire, garantissant que les modifications arrivent une à la fois.
Les serrures de spin posent un problème car les blocs de filage de la CPU de filetage de tout autre travail. Bien que le noyau Linux fournit des services multitâches aux programmes d'espace utilisateur exécutant en cours, cette installation multitâche à usage général ne s'étend pas au code du noyau.
Cette situation change et a été pour la plupart de l'existence de Linux. Par le biais de Linux 2.0, le noyau était presque purement un programme à une seule tâche: chaque fois que le CPU exécutait du code du noyau, un seul noyau de la CPU a été utilisé, car il y avait une seule serrure de spin protégeant toutes les ressources partagées, appelée le grand verrouillage du noyau (BKL ). Commençant par Linux 2.2, le BKL est lentement divisé en de nombreux verrous indépendants qui protègent chacun une classe de ressources plus ciblée. Aujourd'hui, avec le noyau 2.6, la BKL existe toujours, mais elle n'est utilisée que par un code vraiment ancien qui ne peut pas être facilement déplacé vers un blocage plus granulaire. Il est désormais tout à fait possible pour une boîte multicœur d'avoir chaque CPU exécutant du code de noyau utile.
Il y a une limite à l'utilité de briser le BKL car le noyau Linux manque de multitâche général. Si un noyau de la CPU est bloqué à la filature sur une serrure de spin de noyau, elle ne peut pas être rebondée, pour aller faire quelque chose d'autre jusqu'à ce que le verrou soit libéré. Il se trouve juste et tourne jusqu'à ce que la serrure soit libérée.
Les serrures de spin peuvent effectivement transformer une boîte de monstre 16-noyau dans une zone à base unique, si la charge de travail est telle que chaque noyau attend toujours une seule serrure de spin. C'est la limite principale à l'évolutivité du noyau Linux: Doubler les cœurs de processeur de 2 à 410 doublera probablement presque la vitesse d'une boîte Linux, mais le doublera de 16 à 32 probablement ne sera probablement pas, avec la plupart des charges de travail.
Une serrure de spin est quand un processus interroge continuellement pour qu'un verrou soit éliminé. Il est considéré comme mauvais parce que le processus consomme des cycles (généralement) inutiles. Ce n'est pas spécifique à Linux, mais un modèle de programmation général. Et bien qu'il soit généralement considéré comme une mauvaise pratique, c'est en fait la solution correcte; Il existe des cas où le coût de l'utilisation du planificateur est plus élevé (en termes de cycles de processeur) que le coût des quelques cycles que le spinlock devrait durer.
Exemple d'un spinlock:
#!/bin/sh
#wait for some program to clear a lock before doing stuff
while [ -f /var/run/example.lock ]; do
sleep 1
done
#do stuff
Il y a souvent un moyen d'éviter une serrure de spin. Pour cet exemple particulier, il y a un outil Linux appelé Inotiatinywait (il n'est généralement pas installé par défaut). S'il était écrit en C, vous utiliseriez simplement l'API Inotify API que Linux fournit.
Le même exemple, en utilisant InotiplerWait montre comment accomplir la même chose sans une serrure de spin:
#/bin/sh
inotifywait -e delete_self /var/run/example.lock
#do stuff
Lorsqu'un fil tente d'acquérir une serrure, trois choses peuvent se produire si elle échoue, elle peut essayer de bloquer, il peut essayer de continuer, il peut essayer ensuite de vous endormir dire au système d'exploitation de les élever lorsque un événement se réchauffe.
Maintenant, un essai et continuer utilise beaucoup moins de temps alors un essai et un blocage. Disons pour le moment qu'un "essaie de continuer" prendra i de l'unité de temps et d'un "essai et de bloc" prendra une centaine.
Maintenant, laissez-nous pour le moment supposer que sur la moyenne, un thread prenda 4 unités de temps tenant la serrure. Il est inutile d'attendre 100 unités. Donc, au lieu de cela, vous écrivez une boucle de "essayer de continuer". En fin de compte, vous allez généralement acquérir la serrure. Ceci est une serrure de spin. Il est appelé que parce que le fil continue de tourner en place jusqu'à ce qu'il obtienne la serrure.
Une mesure de sécurité ajoutée consiste à limiter le nombre de fois où la boucle fonctionne. Donc, dans l'exemple, vous faites une boucle à la boucle, par exemple six fois, s'il échoue, vous "essayez de bloquer".
Si vous savez qu'un thread tiendra toujours la serrure pour dire 200 unités, vous perdez la durée de l'ordinateur pour chaque essai et continuez.
Donc, à la fin, une serrure de spin peut être très efficace ou inutile. Il est gaspillé lorsque le temps "typique" de maintenir une serrure est plus élevé que le temps nécessaire pour "essayer de bloquer". Il est efficace lorsque le temps typique de maintenir un verrou est beaucoup plus petit que le temps d'essayer de "essayer".
PS: le livre à lire sur les threads est "un apprêt à fil", si vous pouvez toujours le trouver.
A verrouillage est un moyen de synchroniser deux tâches ou plus (processus, threads). Plus précisément, lorsque les deux tâches ont besoin d'un accès intermittent à une ressource qui ne peut être utilisée que par une tâche à la fois, c'est un moyen pour les tâches d'organiser ne pas utiliser la ressource en même temps. Pour accéder à la ressource, une tâche doit procéder comme suit:
take the lock
use the resource
release the lock
Prendre un verrou n'est pas possible si une autre tâche l'a déjà prise. (Pensez à la serrure comme un objet de jeton physique. Soit l'objet est dans un tiroir, soit quelqu'un l'a dans leur main. Seule la personne qui détient l'objet peut accéder à la ressource.) Alors "prenez vraiment" "signifie vraiment" attendre Personne d'autre n'a la serrure, puis prenez-la ".
À partir d'un point de vue de haut niveau, il existe deux façons majeures pour mettre en œuvre des serrures: spinlocks et conditions. Avec Spinlocks , prenant le verrou signifie simplement "filer" (c'est-à-dire ne rien faire dans une boucle) jusqu'à ce que personne d'autre ait la serrure. Avec des conditions, si une tâche tente de prendre la serrure mais est bloquée car une autre tâche le détient, le nouveau venu entre une attente d'attente; Les signaux de fonctionnement de la libération à toute tâche en attente que la serrure est maintenant disponible.
(Ces explications ne suffisent pas pour vous permettre de mettre en œuvre une serrure, car je n'ai rien dit sur l'atomicité. Mais l'atomicité n'est pas importante ici.)
Les spinlocks sont évidemment gaspillés: la tâche en attente continue de vérifier si la serrure est prise. Alors pourquoi et quand est-il utilisé? Les spinlocks sont souvent très bon marché pour obtenir dans le cas où le verrou n'est pas tenu. Cela le rend attrayant lorsque la chance de la maintenance est petite. De plus, les spinlocks ne sont viables que si l'obtention de la serrure ne devrait pas être longue. Donc, les spinlocks ont tendance à être utilisés dans des situations où ils resteront tenus très peu de temps, de sorte que la plupart des tentatives devraient réussir le premier essai et ceux qui ont besoin d'une attente n'attendent pas longtemps.
Il y a une bonne explication des spinlocks et d'autres mécanismes de concurrence du noyau Linux dans pilotes de périphérique Linux , chapitre 5.
Un spinlock est un verrou qui fonctionne en désactivant le planificateur et éventuellement des interruptions (variante IRQSave) sur ce noyau particulier que le verrou est acquis sur. Il est différent d'un mutex dans lequel il désactive la planification afin que votre thread ne puisse fonctionner que lorsque Spinlock est tenu. Un mutex permet de programmer d'autres threads de priorité plus élevés pendant qu'il est tenu mais ne leur permet pas d'exécuter simultanément la section protégée. Étant donné que les spirlocks désactivent le multitâche, vous ne pouvez pas prendre un spinlock, puis appeler un autre code qui tentera d'acquérir un mutex. Votre code à l'intérieur de la section Spinlock ne doit jamais dormir (le code dort généralement lorsqu'il rencontre un mutex verrouillé ou un sémaphore vide).
Une autre différence avec un mutex est que les threads typiquement file d'attente pour un mutex, donc un mutex sous une file d'attente. Tandis que Spinlock garantit simplement qu'aucun autre thread ne fonctionne même si cela doit. Par conséquent, vous ne devez jamais retenir un spinlock lorsque vous appelez des fonctions en dehors de votre fichier que vous n'êtes pas sûr de ne pas dormir.
Lorsque vous souhaitez partager votre spinlock avec une interruption, vous devez utiliser IRQSave Variant. Cela ne permettra pas de désactiver le planificateur mais ne désactivera également les interruptions. Cela a du sens bien? Spinlock fonctionne en s'assurant que rien d'autre ne fonctionnera. Si vous ne voulez pas qu'une interruption vous exécute vous désactivez et continuez dans la section critique.
Sur la machine multicœur, un spinlock va effectivement en attente d'un autre noyau qui maintient le verrou pour la libérer. Cette filature ne se produit que sur les machines multicœurs car sur des noyau monoprotors, il ne peut pas arriver (vous détenez Spintlock et continuez ou vous ne courez jamais tant que le verrouillage n'est pas libéré).
Spinlock n'est pas gaspillé où cela a du sens. Pour de très petites sections critiques, il serait inutile d'attribuer une file d'attente de tâches mutex par rapport à la simple suspension du planificateur pour quelques microsecondes qu'il faut pour terminer le travail important. Si vous avez besoin de dormir ou de maintenir la serrure sur une opération IO (qui peut dormir), utilisez un mutex. Ne verrouillez certainement jamais un spinlock, puis essayez de le libérer à l'intérieur d'une interruption. Bien que cela fonctionnera, ce sera comme la merde Arduino de tout (Flagnotset); Dans ce cas, utilisez un sémaphore.
Prenez un spinlock lorsque vous avez besoin d'une exclusion mutuelle simple pour des blocs de transaction de mémoire. Prenez un mutex Lorsque vous souhaitez que plusieurs threads s'arrêtent juste avant une serrure Mutex, puis le fil de priorité la plus élevée à choisir pour continuer lorsque Mutex devient libre et lorsque vous vous verrouillez et libérez dans le même fil. Prenez un sémaphore lorsque vous avez l'intention de le poster dans un fil ou une interruption et prenez-la dans un autre fil. Il est trois manières légèrement différentes d'assurer l'exclusion mutuelle et elles sont utilisées à des fins légèrement différentes.