Herkömmliche Unix-Kernel hatten eine einzelne Sperre, die von einem Thread gehalten wurde, während der Kernel-Code lief. Daher könnte kein anderer Kernel-Code diesen Thread unterbrechen.
Dies erleichterte das Entwerfen des Kernels, da Sie wussten, dass während ein Thread Kernel-Ressourcen verwendet, kein anderer Thread dies tut. Daher können sich die verschiedenen Threads nicht gegenseitig die Arbeit vermasseln.
In Einzelprozessorsystemen verursacht dies nicht allzu viele Probleme.
In Multiprozessorsystemen kann es jedoch vorkommen, dass mehrere Threads auf verschiedenen Prozessoren oder Kernen gleichzeitig Kernelcode ausführen möchten. Das bedeutet, dass Sie je nach Art der Arbeitslast viele Prozessoren haben können, aber alle verbringen die meiste Zeit damit, aufeinander zu warten.
In Linux 2.6 wurden die Kernel-Ressourcen in viel kleinere Einheiten aufgeteilt, durch einzelne Sperren geschützt, und der Kernel-Code wurde überprüft, um sicherzustellen, dass Sperren nur gehalten werden, während die entsprechenden Ressourcen verwendet werden. So müssen nun verschiedene Prozessoren nur noch aufeinander warten, wenn sie auf dieselbe Ressource (zB Hardware-Ressource) zugreifen wollen.
Vor der Linux-Kernel-Version 2.5.4 war der Linux-Kernel nicht präventiv, was bedeutet, dass ein Prozess, der im Kernelmodus ausgeführt wird, nicht aus dem Prozessor verschoben werden kann, bis er selbst den Prozessor verlässt oder anfängt, auf den Abschluss einer Eingabe-Ausgabe-Operation zu warten.
Im Allgemeinen kann ein Prozess im Benutzermodus mithilfe von Systemaufrufen in den Kernelmodus wechseln. Früher, als der Kernel nicht präemptiv war, konnte ein Prozess mit niedrigerer Priorität die Priorität eines Prozesses mit höherer Priorität umkehren, indem er ihm den Zugriff auf den Prozessor verweigerte, indem er wiederholt Systemaufrufe aufrief und im Kernelmodus blieb. Selbst wenn die Zeitscheibe des Prozesses mit niedrigerer Priorität abgelaufen wäre, würde er weiterlaufen, bis er seine Arbeit im Kernel abgeschlossen oder freiwillig die Kontrolle abgegeben hätte. Wenn der Prozess mit höherer Priorität, der darauf wartet, ausgeführt zu werden, ein Texteditor ist, in den der Benutzer tippt, oder ein MP3-Player, der bereit ist, seinen Audiopuffer aufzufüllen, ist das Ergebnis eine schlechte interaktive Leistung. Auf diese Weise war der nicht präemptive Kernel zu dieser Zeit ein großer Nachteil.
Stellen Sie sich die einfache Ansicht von präventivem Multitasking vor. Wir haben zwei Benutzertasks, die beide die ganze Zeit laufen, ohne I/O zu verwenden oder Kernel-Aufrufe auszuführen. Diese beiden Tasks müssen nichts Besonderes tun, um auf einem Multitasking-Betriebssystem ausgeführt werden zu können. Der Kernel, der normalerweise auf einem Timer-Interrupt basiert, entscheidet einfach, dass es an der Zeit ist, dass eine Aufgabe angehalten wird, um eine andere auszuführen. Die fragliche Aufgabe weiß überhaupt nicht, dass etwas passiert ist.
Die meisten Tasks stellen jedoch gelegentlich über Syscalls Anfragen an den Kernel. Wenn dies geschieht, existiert derselbe Benutzerkontext, aber die CPU führt Kernel-Code für diese Aufgabe aus.
Ältere Linux-Kernel erlaubten niemals die vorzeitige Beendigung einer Aufgabe, während sie mit der Ausführung von Kernel-Code beschäftigt war. (Beachten Sie, dass E/A-Operationen immer freiwillig neu geplant werden. Ich spreche von einem Fall, in dem der Kernel-Code einige CPU-intensive Operationen wie das Sortieren einer Liste hat.)
Wenn das System zulässt, dass diese Aufgabe unterbrochen wird, während es Kernel-Code ausführt, dann haben wir einen sogenannten „preemptiven Kernel“. Ein solches System ist immun gegen unvorhersehbare Verzögerungen, die während Systemaufrufen auftreten können, daher ist es möglicherweise besser für eingebettete oder Echtzeitaufgaben geeignet.
Wenn zum Beispiel auf einer bestimmten CPU zwei Aufgaben verfügbar sind und eine einen Systemaufruf übernimmt, der 5 ms dauert, und die andere eine MP3-Player-Anwendung ist, die alle 2 ms die Audioleitung speisen muss, hören Sie möglicherweise stotterndes Audio.
Das Argument gegen Preemption ist, dass der gesamte Kernel-Code, der im Aufgabenkontext aufgerufen werden könnte, in der Lage sein muss, die Preemption zu überleben – es gibt beispielsweise eine Menge schlechten Gerätetreibercodes, der besser dran wäre, wenn er immer in der Lage wäre, eine Operation vorher abzuschließen Zulassen, dass eine andere Aufgabe auf diesem Prozessor ausgeführt wird. (Bei Mehrprozessorsystemen heutzutage eher die Regel als die Ausnahme, muss der gesamte Kernel-Code wiedereintrittsfähig sein, sodass dieses Argument heute nicht mehr so relevant ist.) Außerdem könnte das gleiche Ziel erreicht werden, indem die Systemaufrufe mit schlecht verbessert werden Latenz, vielleicht ist Vorkaufsrecht unnötig.
Ein Kompromiss ist CONFIG_PREEMPT_VOLUNTARY, das einen Taskwechsel an bestimmten Stellen innerhalb des Kernels erlaubt, aber nicht überall. Wenn es nur wenige Stellen gibt, an denen Kernel-Code stecken bleiben könnte, ist dies eine kostengünstige Möglichkeit, die Latenz zu reduzieren und gleichzeitig die Komplexität überschaubar zu halten.
Die Präemption ermöglicht es dem Kernel, den EINDRUCK von Parallelität zu vermitteln:Sie haben nur einen Prozessor (sagen wir vor einem Jahrzehnt), aber Sie haben das Gefühl, dass alle Ihre Prozesse gleichzeitig laufen. Das liegt daran, dass der Kernel die Ausführung von einem Prozess vorwegnimmt (dh die Ausführung ausnimmt), um sie an den nächsten zu übergeben (vielleicht entsprechend ihrer Priorität).
BEARBEITEN Nicht preemptive Kernel warten darauf, dass Prozesse die Hand zurückgeben (dh während Systemaufrufen), also wenn Ihr Prozess viele Daten berechnet und keine Art von yield
aufruft -Funktion können die anderen Prozesse ihre Aufrufe nicht ausführen. Solche Systeme werden als kooperativ bezeichnet weil sie die Zusammenarbeit der Prozesse verlangen, um die Gerechtigkeit der Ausführungszeit zu gewährleisten
BEARBEITEN 2 Das Hauptziel der Präemption besteht darin, die Reaktivität des Systems bei mehreren Aufgaben zu verbessern, was gut für Endbenutzer ist, während Server andererseits den höchsten Durchsatz erzielen möchten, also brauchen sie ihn nicht:(von der Linux-Kernel-Konfiguration)
- Präemptiver Kernel (Desktop mit niedriger Latenz)
- Freiwillige Kernel-Präemption (Desktop)
- Keine erzwungene Präemption (Server)