Heim > Artikel > System-Tutorial > Detaillierte Erläuterung des RCU-Mechanismus im Linux-Kernel
Der Linux-Kernel ist ein komplexes System, das sich mit einer Vielzahl von Parallelitätsproblemen befassen muss, wie z. B. Prozessplanung, Speicherverwaltung, Gerätetreiber, Netzwerkprotokolle usw. Um die Konsistenz und Korrektheit der Daten sicherzustellen, bietet der Linux-Kernel verschiedene Synchronisationsmechanismen wie Spin-Locks, Semaphore, Lese-/Schreibsperren usw. Diese Synchronisationsmechanismen weisen jedoch einige Mängel auf, wie zum Beispiel:
Gibt es also einen besseren Synchronisierungsmechanismus? Die Antwort lautet: Ja, das ist RCU (Read Copy Update). RCU ist ein Synchronisationsmechanismus, der auf dem Publish-Subscribe-Modell basiert und effiziente Lesevorgänge und Aktualisierungsvorgänge mit geringer Latenz erreichen kann. Die Grundidee von RCU ist folgende:
1. 2. static inline void rcu_read_lock(void) 3. { 4. __rcu_read_lock(); 5. __acquire(RCU); 6. rcu_read_acquire(); 7. }
.
Die nächste interessante Frage zu RCU ist: Wann kann der alte Zeiger freigegeben werden? Die Antwort darauf, die ich in vielen Büchern gesehen habe, lautet: Wenn auf allen Prozessoren im System ein Prozesswechsel stattfindet. Diese stilisierte Antwort verwirrt oft Leser, die mit dem RCU-Mechanismus noch nicht vertraut sind. Warum müssen wir warten, bis auf allen Prozessoren ein Prozesswechsel erfolgt, bevor wir die Rückruffunktion aufrufen, um den alten Zeiger freizugeben? Dies wird tatsächlich durch die Entwurfsregeln von RCU bestimmt: Alle Verweise auf alte Zeiger können nur im kritischen Abschnitt auftreten, der in rcu_read_lock und rcu_read_unlock enthalten ist, und in diesem kritischen Abschnitt ist kein Prozesswechsel möglich. , Sobald der kritische Abschnitt vorhanden ist, sollte dies nicht der Fall sein haben keinen Bezug mehr zum alten Zeiger p. Offensichtlich erfordert diese Regel, dass der Leser den Prozess im kritischen Abschnitt nicht wechseln kann, da bei einem Prozesswechsel die Rückruffunktion aufgerufen werden kann, die den alten Zeiger freigibt, wodurch der alte Zeiger freigegeben wird, wenn der Prozess umgeschaltet wird neu geplant wird, verweist es möglicherweise auf einen freigegebenen Speicherplatz.
Jetzt sehen wir, warum rcu_read_lock nur die Kernel-Präemptivität ausschalten muss, weil es es unmöglich macht, den aktuellen Prozess zu wechseln und zu entfernen, selbst wenn im kritischen Abschnitt ein Interrupt auftritt. Die Kernel-Entwickler, oder besser gesagt die Designer von RCU, können nur eine begrenzte Menge tun. Der nächste Schritt liegt in der Verantwortung des Benutzers. Wenn eine Funktion im kritischen Bereich der RCU aufgerufen wird, schläft die Funktion möglicherweise ein, wodurch die Entwurfsregeln der RCU verletzt werden und das System in einen instabilen Zustand gerät.
Dies zeigt einmal mehr, dass man, wenn man etwas nutzen möchte, seinen inneren Mechanismus verstehen muss. Auch wenn es jetzt keine Probleme mit dem Programm gibt, sind die im System verbliebenen Gefahren wie eine Zeitbombe , was jederzeit passieren kann, insbesondere wenn es lange dauert, bis das Problem plötzlich auftritt. In den meisten Fällen dauert es möglicherweise viel länger, das Problem zu finden, als sich zu beruhigen und die Prinzipien von RCU sorgfältig zu verstehen.
Leser in RCU haben einen höheren Freiheitsgrad als Leser in rwlock. Da der RCU-Leser die Gefühle des Autors beim Zugriff auf eine gemeinsam genutzte Ressource nicht berücksichtigen muss, unterscheidet sich dies vom rwlock-Leser. Der rwlock-Leser muss sicherstellen, dass kein Autor die Ressource bedient, wenn er die gemeinsam genutzte Ressource liest. Der Unterschied zwischen den beiden liegt darin, dass RCU gemeinsame Ressourcen zwischen Lesern und Autoren trennt, während rwlock-Leser und -Autoren von Anfang bis Ende nur eine Kopie der gemeinsam genutzten Ressourcen verwenden . Dies bedeutet auch, dass Autoren in RCU mehr Verantwortung tragen müssen und eine Art gegenseitiger Ausschlussmechanismus zwischen mehreren Autoren eingeführt werden muss, die dieselbe gemeinsam genutzte Ressource aktualisieren, sodass RCU ein „sperrfreier Mechanismus“ ist. Die Aussage ist auf Leser und beschränkt Schriftsteller. Wir sehen also, dass der RCU-Mechanismus in Situationen verwendet werden sollte, in denen es viele Lesevorgänge und relativ wenige Aktualisierungsvorgänge gibt. Zu diesem Zeitpunkt kann RCU die Systemleistung erheblich verbessern, da der Lesevorgang von RCU im Vergleich zu einigen anderen Sperrmechanismen fast keinen Sperraufwand verursacht.
Bei der tatsächlichen Verwendung liegen gemeinsam genutzte Ressourcen häufig in Form verknüpfter Listen vor. Leser und Benutzer sollten diese Kernelfunktionen wie list_add_tail_rcu, list_add_rcu, hlist_replace_rcu usw. verwenden. Informationen zur spezifischen Verwendung finden Sie in einigen Informationen zur Kernel-Programmierung oder zum Gerätetreiber.
In Bezug auf die Veröffentlichung alter Zeiger bietet der Linux-Kernel Benutzern zwei Methoden zur Verwendung: Eine besteht darin, call_rcu aufzurufen, und die andere darin, synchronisiert_rcu aufzurufen. Ersteres ist eine asynchrone Methode. call_rcu fügt die Rückruffunktion, die den alten Zeiger freigibt, in einen Knoten ein und fügt den Knoten dann zur lokalen verknüpften Liste des Prozessors hinzu, der aktuell call_rcu im Softirq-Teil des Taktinterrupts ausführt. , die RCU-Soft-Interrupt-Verarbeitungsfunktion rcu_process_callbacks prüft, ob der aktuelle Prozessor eine Ruhephase durchlaufen hat (Ruhezustand, der die Kernel-Prozessplanung und andere Aspekte umfasst). Die Kernel-Code-Implementierung von RCU bestimmt, ob alle Prozessoren im System eine Ruhephase durchlaufen haben Zeitraum (was bedeutet, dass auf allen Prozessoren ein Prozesswechsel stattgefunden hat, sodass der alte Zeiger zu diesem Zeitpunkt sicher freigegeben werden kann), wird die von call_rcu bereitgestellte Rückruffunktion aufgerufen.
Die Implementierung von synchronisiert_rcu nutzt die Warteschlange. Während der Implementierung wird auch ein Knoten zur lokalen verknüpften Liste des aktuellen Prozessors hinzugefügt. Der Unterschied zu call_rcu besteht darin, dass die Rückruffunktion in diesem Knoten wakeme_after_rcu ist und dann synchronisiert_rcu bleibt in einer Warteschlange, bis ein Prozesswechsel auf allen Prozessoren im System erfolgt, daher wird wakeme_after_rcu von rcu_process_callbacks aufgerufen, um den schlafenden synchronisiert_rcu aufzuwecken. Nach dem Aufwecken weiß synchronisiert_rcu, dass es nun den alten Zeiger freigeben kann.
Wir sehen also, dass die registrierte Rückruffunktion nach der Rückkehr von call_rcu möglicherweise nicht aufgerufen wurde, was bedeutet, dass der alte Zeiger nicht freigegeben wurde und der alte Zeiger nach der Rückkehr von synchronisiert_rcu freigegeben worden sein muss. Ob call_rcu oder synchronisiert_rcu aufgerufen werden soll, hängt daher von den spezifischen Anforderungen und dem aktuellen Kontext ab. Beispielsweise kann die Funktion synchronisiert_rcu nicht im Kontext der Interrupt-Verarbeitung verwendet werden.
In diesem Artikel wird RCU vorgestellt, ein effizienter Synchronisierungsmechanismus im Linux-Kernel. Es handelt sich um einen Synchronisierungsmechanismus, der auf dem Publish-Subscribe-Modell basiert. Wir haben die Grundideen, Schlüsselschnittstellen und Implementierungsdetails von RCU unter prinzipiellen Aspekten analysiert und entsprechende Codebeispiele gegeben. Wir haben auch die Verwendung von RCU in verknüpften Listenoperationen, Timer-Management, Soft-Interrupt-Verarbeitung und anderen Szenarien aus Anwendungssicht vorgestellt und entsprechende Codebeispiele gegeben. Durch das Studium dieses Artikels können wir die grundlegende Verwendung von RCU beherrschen und RCU in der tatsächlichen Entwicklung flexibel verwenden, um effiziente Synchronisierungsanforderungen zu erreichen. Ich hoffe, dieser Artikel ist hilfreich für Sie!
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des RCU-Mechanismus im Linux-Kernel. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!