51 Stimmen

Wie funktionieren die Greenlets?

Wie werden Grünlinge umgesetzt? Python verwendet den C-Stack für den Interpreter und es Heap-allocates Python Stack Frames, aber darüber hinaus, wie es zuweisen/Swap-Stacks, wie es Haken in den Interpreter und Funktionsaufruf Mechanismen, und wie interagiert dies mit C-Erweiterungen? (Irgendwelche Macken)?

Es gibt einige Kommentare am Anfang von greenlet.c in der Quelle, aber sie sind ein bisschen undurchsichtig. FWIW Ich komme aus der Perspektive von jemandem, der mit CPython Interna nicht vertraut ist, aber ist sehr vertraut mit Low-Level-System-Programmierung, C, Threads, Ereignisse, Coroutines/kooperative Threads, Kernel-Programmierung, etc.

(Einige Daten: sie ucontext.h nicht verwenden und sie 2x memcpy, alloc und free bei jedem Kontextwechsel durchführen .)

36voto

Rabih Kodeih Punkte 9008

Wenn ein Python-Programm ausgeführt wird, laufen unter der Haube im Wesentlichen zwei Code-Teile.

Erstens der C-Code des CPython-Interpreters, der den Standard-C-Stack verwendet, um seine internen Stack-Frames zu speichern. Zweitens der eigentliche interpretierte Python-Bytecode, der nicht den C-Stack verwendet, sondern den Heap, um seine Stack-Frames zu speichern. Ein Greenlet ist einfach Standard-Python-Code und verhält sich daher identisch.

In einer typischen Microthreading-Anwendung gibt es Tausende, wenn nicht sogar Millionen von Microthreads (Greenlets), die überall hin wechseln. Jeder Wechsel ist im Wesentlichen gleichbedeutend mit einem Funktionsaufruf mit verzögerter Rückgabe (sozusagen) und verbraucht daher ein wenig Stack. Das Problem ist, dass der C-Stack des Interpreters früher oder später auf einen Stapelüberlauf trifft. Genau darauf zielt die Greenlet-Erweiterung ab, die Teile des Stacks vom/zum Heap hin und her schiebt, um dieses Problem zu vermeiden.

Wie Sie wissen, gibt es drei grundlegende Ereignisse bei Greenlets: einen Laichvorgang, einen Wechsel und eine Rückkehr, also lassen Sie uns diese der Reihe nach betrachten:

A) Ein Laich

Das neu erzeugte Greenlet wird mit seiner eigenen Basisadresse im Stack verbunden (wo wir uns gerade befinden). Abgesehen davon passiert nichts Besonderes. Der Python-Code des neu erzeugten Greenlets verwendet den Heap auf normale Weise, und der Interpreter verwendet weiterhin den C-Stack wie üblich.

B) Ein Schalter

Wenn ein Greenlet von einem schaltenden Greenlet umgeschaltet wird, wird der entsprechende Teil des C-Stapels (ab der Basisadresse des schaltenden Greenlets) in den Heap kopiert. Der kopierte C-Stack-Bereich wird freigegeben und die zuvor vom Interpreter des geschalteten Greenlets gespeicherten Stack-Daten werden vom Heap in den neu freigegebenen C-Stack-Bereich kopiert. Der Python-Code des umgeschalteten Greenlets nutzt den Heap weiterhin auf normale Weise. Natürlich behält der Erweiterungscode den Überblick über all diese Vorgänge (welcher Heap-Bereich zu welchem Greenlet gehört usw.).

C) Eine Rückkehr

Der Stack bleibt unangetastet und der Heap-Bereich des zurückkehrenden Greenlets wird vom Python Garbage Collector freigegeben.

Im Grunde ist dies alles, viele weitere Details und Erklärungen finden Sie unter ( http://www.stackless.com/pipermail/stackless-dev/2004-March/000022.html ) oder einfach durch Lesen des Codes, wie in Alex' Antwort beschrieben.

32voto

Alex Martelli Punkte 805329

Holen Sie sich und studieren Sie die Greenlet's Quellen sehen Sie oben auf der Seite greenlet.c einen langen Kommentar, der in Zeile 16 mit der folgenden Zusammenfassung beginnt...:

Ein PyGreenlet ist ein Bereich von C-Stacks Adressen, die gespeichert werden müssen und wiederhergestellt werden, so dass der gesamte Bereich des Stacks gültige Daten enthält enthält, wenn wir zu ihm wechseln.

und geht weiter zu Zeile 82, in der genau das zusammengefasst wird, wonach Sie fragen. Haben Sie diese Zeilen (und die folgenden 1000+, die sie umsetzen;-)... studiert? Ich sehe keine Möglichkeit, diese 66 Zeilen weiter zu kürzen, ohne dass sie ihren Sinn verlieren, und ich sehe auch keinen Mehrwert darin, sie hier zu kopieren und einzufügen.

Grundsätzlich werden Sie sehen, dass es kein wirkliches "Hooking" gibt (der Stack auf C-Ebene wird sozusagen "vor der Nase des Interpreters" hin- und hergeschaltet), mit Ausnahme der heiklen Interaktionen mit dem Thread-Status in Multi-Thread-Code, und das Speichern und Wiederherstellen des Status eines Greenlets vom/zum Stack basiert auf memcpy Aufrufen sowie einigen Aufrufen an den Python-Speicherverwalter, um Speicherplatz zuzuweisen/neu zuzuweisen und freizugeben, der vom Stack kommt oder dorthin zurückgeht. Die drei Funktionen in den Zeilen 227-295 erledigen die grobe Arbeit, und sie sind in ein paar C-Makros in den Zeilen 298-310 verpackt, "um die Wartung zu vereinfachen", wie der Kommentar dort sagt.

Die Schnittstelle, über die andere C-Erweiterungen mit der Greenlet-Erweiterung interagieren können, ist in den Zeilen 956-1045 implementiert und wird über die "CObject API" (über greenlet.h (natürlich) dokumentiert aquí .

3 Stimmen

Dieser Kommentarblock ist für mich verwirrend und beantwortet meine Fragen nicht wirklich. Ich hatte nur auf eine kurze Zusammenfassung/Antwort auf hohem Niveau gehofft. Trotzdem danke für die Hinweise - ich hoffe, sie sind für andere nützlich (oder für mich selbst, wenn ich mehr Zeit finde, in die Quellen einzutauchen).

0 Stimmen

@Yang, diese 86 Zeilen sind eine prägnante Zusammenfassung auf hohem Niveau, die die meisten Highlights der 1410 Codezeilen enthält, aus denen sich das Programm zusammensetzt .c y .h Dateien! "Stack slices are saved by memcpy to memory that's allocated and reallocated by the Python memory manager, and restored by memcpy back into the stack (then the Python memory is freed)" ist sogar noch prägnanter und auf höherem Niveau (aber das habe ich alles schon in meiner Antwort gesagt!), aber offensichtlich fehlen einige wichtige Details (da es zwei Zeilen Text sind, nicht 86;-). Welchen magischen Text erwarten Sie denn, der dazwischen liegt und Sie glücklich macht?!

8 Stimmen

Zunächst einmal: Was sind "Greenlet-Stapeldaten"? Ist das nur Buchhaltung für das Greenlet? Oder gehören dazu auch bestimmte C-Stack-Frames? Was ist der "richtige Platz eines Greenlets im Stack"? Warum gibt es immer zwei Greenlet-Blöcke/Warum ist der ältere auf dem Heap? Was sind "Daten ohne Bezug zu diesem Greenlet" unter "Greenlet-Stack-Daten"? Was ist der Unterschied zwischen "nicht verwandten Daten" und "neueren Daten"? Etc. Es ist eine kleine Menge von C, aber ich bin auch beschäftigt und dies ist überhaupt nicht auf meine aktuelle Arbeit bezogen - nur fragen aus Neugierde. Die Frage ist mir einfach in den Kopf gekommen. Nochmals, ich werde später gerne in die Quellen eintauchen, sobald ich Zeit finde.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X