Wie „lauschen“ Webserver auf IP-Adressen, unterbrechen oder rufen ab?

87

Ich versuche, die niedrigeren Details von Webservern zu verstehen. Ich frage mich, ob ein Server, wie Apache, ständig nach neuen Anfragen fragt oder ob er von einem Interrupt-System funktioniert. Wenn es sich um einen Interrupt handelt, was löst den Interrupt aus, ist es der Netzwerkkartentreiber?

user2202911
quelle
1
Das Schlüsselwort zu verstehen ist "Server" . Im Server-Client-Modell (im Vergleich zum Master-Slave-Modell) wartet der Server auf Anforderungen von Clients. Diese Anforderungen sind Ereignisse , die bearbeitet werden müssen. Ein Webserver ist ein Anwendungsprogramm. Ihre Frage kombiniert Anwendungs-SW mit Hardware-Terminologie (z. B. Interrupt und NIC), anstatt verwandte Konzepte auf derselben Abstraktionsschicht zu belassen. Der NIC-Treiber verwendet unter Umständen tatsächlich Abfragen. Beispielsweise werden Linux-NAPI-Treiber bei einer Flut von Paketen auf Abfragen zurückgeführt. Dies ist jedoch für die Ereignisverarbeitungsanwendung SW irrelevant.
Sägemehl
1
@sawdust Sehr interessant. Die Frage ist wirklich dazu gedacht, den Zusammenhang zwischen den SW- und HW-Prozessen zu verstehen
user2202911
1
Es ist sehr ähnlich der Art und Weise, wie Befehlszeilenprogramme (und andere GUI-Programme) die Tastatur anhören. Insbesondere in einem Fenstersystem, in dem der Kernel die Daten vom Tastaturgerät empfängt und an den Fenstermanager weitergibt, der das fokussierte Fenster identifiziert und die Daten an dieses Fenster weitergibt.
G-Man
@ G-Man: Ich theoretisch ja. In Wirklichkeit tippen die meisten Schreibkräfte nicht mit 1 Gbit / s, was zwei unterschiedliche Architekturen rechtfertigt. Eine saubere, flexible und langsame, eine ungeschickte, aber schnelle.
MSalters

Antworten:

181

Die kurze Antwort lautet: eine Art Interrupt-System. Im Wesentlichen verwenden sie blockierende E / A, dh sie schlafen (blockieren), während sie auf neue Daten warten.

  1. Der Server erstellt einen Listening-Socket und blockiert ihn, während er auf neue Verbindungen wartet. Während dieser Zeit versetzt der Kernel den Prozess in einen unterbrechbaren Ruhezustand und führt andere Prozesse aus. Dies ist ein wichtiger Punkt: Wenn die Prozessabfrage kontinuierlich durchgeführt wird, wird die CPU verschwendet. Der Kernel kann die Systemressourcen effizienter nutzen, indem er den Prozess blockiert, bis Arbeit für ihn zu erledigen ist.

  2. Wenn neue Daten im Netzwerk ankommen, gibt die Netzwerkkarte einen Interrupt aus.

  3. Nachdem die Netzwerkkarte unterbrochen wurde, liest der Kernel über den Netzwerkkartentreiber die neuen Daten von der Netzwerkkarte und speichert sie im Speicher. (Dies muss schnell erledigt werden und wird im Allgemeinen im Interrupt-Handler erledigt.)

  4. Der Kernel verarbeitet die neu eingetroffenen Daten und ordnet sie einem Socket zu. Ein Prozess, der auf diesem Socket blockiert wird, wird als ausführbar markiert, was bedeutet, dass er jetzt ausgeführt werden kann. Es wird nicht unbedingt sofort ausgeführt (der Kernel entscheidet sich möglicherweise dafür, noch andere Prozesse auszuführen).

  5. In seiner Freizeit wird der Kernel den blockierten Webserver-Prozess aktivieren. (Da es jetzt lauffähig ist.)

  6. Der Webserver-Prozess wird so ausgeführt, als ob keine Zeit verstrichen wäre. Sein blockierender Systemaufruf kehrt zurück und verarbeitet alle neuen Daten. Fahren Sie dann mit Schritt 1 fort.

Greg Bowser
quelle
18
+1 für eine klare Abgrenzung des Kernels gegenüber dem Webserver-Prozess.
Russell Borogove
13
Ich kann nicht glauben, dass etwas so Komplexes wie dieses so klar und einfach zusammengefasst werden kann, aber Sie haben es getan. +1
Brandon
8
+1 Gute Antwort. Außerdem können die Schritte zwischen 2 und 3 bei modernen Netzwerkkarten, Betriebssystemen und Treibern etwas komplexer werden. Bei NAPI unter Linux werden die Pakete beispielsweise nicht im Interrupt-Kontext empfangen. Stattdessen sagt der Kernel: "Okay, NIC, ich verstehe, dass Sie Daten haben. Beenden Sie das Abhören (deaktivieren Sie die Interrupt-Quelle), und ich bin in Kürze zurück, um dieses Paket und alle nachfolgenden Pakete, die möglicherweise vor mir eintreffen, abzurufen."
Jonathon Reinhart
8
Leichter Nitpick: Es ist nicht wirklich notwendig zu blocken. Sobald der Serverprozess einen Listening-Socket erstellt hat, akzeptiert der Kernel SYNs an diesem Port, auch wenn Sie nicht blockiert sind accept. Sie sind (zum Glück, oder es wäre total beschissen!) Unabhängige, asynchron laufende Aufgaben. Wenn Verbindungen eingehen, werden sie in eine Warteschlange gestellt, aus der sie abgerufen werden accept. Nur wenn es keine gibt, blockiert es.
Damon
3
"Liest die neuen Daten von der Netzwerkkarte und speichert sie im Speicher. (Dies muss schnell erfolgen und wird im Allgemeinen im Interrupt-Handler verarbeitet.)" Wird dies nicht mit direktem Speicherzugriff durchgeführt?
Siyuan Ren
9

Es gibt ziemlich viele "niedrigere" Details.

Stellen Sie sich zunächst vor, der Kernel verfügt über eine Liste von Prozessen. Einige dieser Prozesse werden zu einem bestimmten Zeitpunkt ausgeführt, andere nicht. Der Kernel lässt jedem laufenden Prozess einen Teil der CPU-Zeit zu, unterbricht ihn dann und wechselt zum nächsten. Wenn es keine ausführbaren Prozesse gibt, gibt der Kernel wahrscheinlich eine Anweisung wie HLT an die CPU aus, die die CPU anhält, bis ein Hardware-Interrupt auftritt.

Irgendwo auf dem Server befindet sich ein Systemaufruf mit der Aufschrift "Gib mir etwas zu tun". Es gibt zwei Kategorien von Möglichkeiten, wie dies durchgeführt werden kann. Im Fall von Apache ruft es accepteinen Socket auf, den Apache zuvor geöffnet hat, und überwacht wahrscheinlich Port 80. Der Kernel verwaltet eine Warteschlange mit Verbindungsversuchen und fügt diese Warteschlange jedes Mal hinzu, wenn ein TCP-SYN empfangen wird. Wie der Kernel weiß, dass eine TCP-SYN empfangen wurde, hängt vom Gerätetreiber ab. Bei vielen Netzwerkkarten liegt wahrscheinlich ein Hardware-Interrupt vor, wenn Netzwerkdaten empfangen werden.

acceptbittet den Kernel, mir den nächsten Verbindungsaufbau zurückzugeben. Wenn die Warteschlange nicht leer war, acceptkehrt sie sofort zurück. Wenn die Warteschlange leer ist, wird der Prozess (Apache) aus der Liste der ausgeführten Prozesse entfernt. Wenn später eine Verbindung hergestellt wird, wird der Vorgang fortgesetzt. Dies wird als "Blockieren" bezeichnet, da der aufrufende Prozess accept()wie eine Funktion aussieht, die erst zurückkehrt, wenn ein Ergebnis vorliegt, das in einiger Zeit vorliegen könnte. Während dieser Zeit kann der Prozess nichts anderes tun.

Nach der acceptRückkehr weiß Apache, dass jemand versucht, eine Verbindung herzustellen. Anschließend wird fork aufgerufen , um den Apache-Prozess in zwei identische Prozesse aufzuteilen. Einer dieser Prozesse verarbeitet die HTTP-Anforderung, der andere ruft accepterneut auf, um die nächste Verbindung herzustellen. Daher gibt es immer einen Master-Prozess, der nur acceptUnterprozesse aufruft und erzeugt, und dann gibt es für jede Anforderung einen Unterprozess.

Dies ist eine Vereinfachung: Es ist möglich, dies mit Threads anstelle von Prozessen zu tun, und es ist auch möglich, dies forkvorher zu tun, damit ein Arbeitsprozess bereit ist, wenn eine Anforderung empfangen wird, wodurch der Startaufwand verringert wird. Abhängig davon, wie Apache konfiguriert ist, kann es eines dieser Dinge tun.

Dies ist die erste allgemeine Kategorie, und sie wird als E / A- Blockierung bezeichnet, da Systemaufrufe wie acceptund readund write, die auf Sockets ausgeführt werden, den Prozess anhalten, bis sie etwas zurückgeben können.

Der andere breite Weg, dies zu tun, wird als nicht blockierende oder ereignisbasierte oder asynchrone E / A bezeichnet . Dies wird mit Systemaufrufen wie selectoder implementiert epoll. Diese tun jeweils dasselbe: Sie geben ihnen eine Liste von Sockets (oder allgemein Dateideskriptoren) und was Sie damit tun möchten, und der Kernel blockiert, bis er bereit ist, eines dieser Dinge zu tun.

Bei diesem Modell können Sie dem Kernel (mit epoll) mitteilen : "Sagen Sie mir, wenn eine neue Verbindung an Port 80 besteht oder wenn neue Daten auf einer dieser 9471 anderen Verbindungen gelesen werden sollen, die ich geöffnet habe". epollblockiert, bis eines dieser Dinge fertig ist, dann machst du es. Dann wiederholst du. Systemaufrufe mögen acceptund readund werden writeniemals blockiert, zum Teil, weil Sie bei jedem Aufruf epollnur erfahren haben, dass sie bereit sind, sodass es keinen Grund zum Blockieren gibt, und weil Sie beim Öffnen des Sockets oder der von Ihnen angegebenen Datei angeben, dass Sie sie möchten Im nicht blockierenden Modus schlagen diese Anrufe fehl, EWOULDBLOCKanstatt zu blockieren.

Der Vorteil dieses Modells ist, dass Sie nur einen Prozess benötigen. Dies bedeutet, dass Sie nicht für jede Anforderung eine Stapel- und Kernelstruktur zuweisen müssen. Nginx und HAProxy verwenden dieses Modell und es ist ein großer Grund, warum sie mit so viel mehr Verbindungen als Apache auf ähnlicher Hardware umgehen können.

Phil Frost
quelle