Heim  >  Artikel  >  Backend-Entwicklung  >  Entdecken Sie den PHP-Lebenszyklus

Entdecken Sie den PHP-Lebenszyklus

coldplay.xixi
coldplay.xixinach vorne
2020-07-28 16:38:082123Durchsuche

Entdecken Sie den PHP-Lebenszyklus

Den PHP-Lebenszyklus lernen

Der Lebenszyklus von PHP ist ein sehr komplexer Prozess, und sein Lebenszyklus sollte von denjenigen gemeistert werden, die daran interessiert sind Benutze es. Der Hauptinhalt ist wie folgt:

PHP-Startup. Wenn Sie die CLI oder FPM ausführen, wird C main() ausgeführt. Wenn es als Modul auf einem Netzwerkserver ausgeführt wird, wie bei der apxs2-SAPI (Apache 2), startet PHP kurz nach dem Start von Apache und beginnt mit der Ausführung der Startsequenz seiner Module, zu denen auch PHP gehört. Der Start wird intern als Modul-Startschritt bezeichnet. Wir kürzen ihn auch als MINIT-Schritt ab.

Nach dem Start wartet PHP auf die Bearbeitung einer/mehrerer Anfragen. Wenn wir über PHP CLI sprechen, gibt es nur eine Anfrage: das aktuell auszuführende Skript. Aber wenn wir über eine Webumgebung sprechen – es sollte PHP-FPM oder ein Webservermodul sein –, kann PHP mehrere Anfragen nacheinander verarbeiten. Es hängt alles davon ab, wie Sie Ihren Webserver konfigurieren: Sie können ihn anweisen, eine unbegrenzte Anzahl von Anfragen oder eine bestimmte Anzahl von Anfragen zu bearbeiten, bevor er den Prozess herunterfährt und wiederverwendet. Jedes Mal, wenn eine neue Anfrage in einem Thread verarbeitet wird, führt PHP den Anfrageinitiierungsschritt aus. Wir nennen es RINIT.

Verwandte Lernempfehlungen: PHP-Programmierung vom Anfänger bis zum Experten

Die Anfrage wird bearbeitet und (vielleicht) einige Inhalte generiert, OK. Es ist Zeit, die Anfrage zu schließen und sich auf die Bearbeitung einer weiteren Anfrage vorzubereiten. Die Abschlussanforderung ruft den Anforderungsabschlussschritt auf. Wir nennen es RSHUTDOWN. ·

Wenn X-Anfragen (eine, Dutzende, Tausende usw.) verarbeitet wurden, schaltet sich PHP endgültig ab und wird beendet. Das Herunterfahren des PHP-Prozesses wird als Schritt zum Herunterfahren des Moduls bezeichnet. Die Abkürzung ist MSHUTDOWN.

Wenn wir diese Schritte zeichnen könnten, könnten wir Folgendes erhalten:

Entdecken Sie den PHP-Lebenszyklus

Paralleles Modell

In der CLI-Umgebung ist alles einfach : Ein Prozess verarbeitet eine Anfrage: Er startet ein separates PHP-Skript und endet dann. Die CLI-Umgebung ist eine Spezialisierung der Webumgebung und komplexer.

Um mehrere Anfragen gleichzeitig zu bearbeiten, müssen Sie ein paralleles Modell ausführen. Es gibt zwei Typen in PHP:

  • Das prozessbasierte Modell Das prozessbasierte Modell
  • Das Thread-basierte Modell Das Thread-basierte Modell

Verwendung Basierend auf dem Prozessmodell isoliert das Betriebssystem jeden PHP-Interpreter in seinen eigenen Prozess. Dieses Modell ist unter Unix sehr verbreitet. Jede Anfrage durchläuft einen eigenen Prozess. PHP-CLI, PHP-FPM und PHP-CGI verwenden dieses Modell.

Im Thread-basierten Modell ist jeder PHP-Interpreter mithilfe einer Thread-Bibliothek in Threads isoliert. Dieses Modell wird hauptsächlich in Windows-Betriebssystemen verwendet, kann aber auch in den meisten Unix-Systemen verwendet werden. Erfordert, dass PHP und seine Erweiterungen im ZTS-Modus erstellt werden.

Dies ist das prozessbasierte Modell:

Entdecken Sie den PHP-Lebenszyklus

Dies ist das Thread-basierte Modell:

Entdecken Sie den PHP-Lebenszyklus

Hinweis

Als Erweiterungsentwickler ist das Multiprozessmodul von PHP keine Option für Sie. Sie müssen es unterstützen. Sie müssen die Ausführung Ihrer Erweiterung in einer Thread-Umgebung, insbesondere unter Windows, ermöglichen und dafür programmiert sein.

PHP-Erweiterungs-Hooks

Wie Sie vielleicht schon vermutet haben, löst die PHP-Engine Ihre Erweiterung an mehreren Lebenszykluspunkten aus. Wir nennen sie Hook-Funktionen. Ihre Erweiterung kann Interesse an bestimmten Lebenszykluspunkten bekunden, indem sie bei der Registrierung bei der Engine Funktions-Hooks deklariert.
Diese Hooks sind deutlich zu erkennen, wenn Sie die PHP-Erweiterungsstruktur (zend_module_entry Struktur) analysieren:

struct _zend_module_entry {
        unsigned short size;
        unsigned int zend_api;
        unsigned char zend_debug;
        unsigned char zts;
        const struct _zend_ini_entry *ini_entry;
        const struct _zend_module_dep *deps;
        const char *name;
        const struct _zend_function_entry *functions;
        int (*module_startup_func)(INIT_FUNC_ARGS);        /* MINIT() */
        int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);   /* MSHUTDOWN() */
        int (*request_startup_func)(INIT_FUNC_ARGS);       /* RINIT() */
        int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);  /* RSHUTDOWN() */
        void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);     /* PHPINFO() */
        const char *version;
        size_t globals_size;
#ifdef ZTS
        ts_rsrc_id* globals_id_ptr;
#else
        void* globals_ptr;
#endif
        void (*globals_ctor)(void *global);                /* GINIT() */
        void (*globals_dtor)(void *global);                /* GSHUTDOWN */
        int (*post_deactivate_func)(void);                 /* PRSHUTDOWN() */
        int module_started;
        unsigned char type;
        void *handle;
        int module_number;
        const char *build_id;
};

Jetzt wollen wir sehen, welche Art von Code Sie in diese Hooks schreiben sollten.

Modulinitialisierung: MINIT()

Dies ist der Startschritt des PHP-Prozesses. Innerhalb des erweiterten MINIT() laden und ordnen Sie alle persistenten Objekte oder Informationen zu, die für jede nachfolgende Anfrage benötigt werden. Die meisten davon werden als schreibgeschützte Objekte zugewiesen.

In MINIT() ist noch kein Thread oder Prozess aufgetaucht, Sie haben also vollen Zugriff auf globale Variablen ohne jeglichen Schutz. Außerdem können Sie anforderungsgebundenen Speicher nicht zuweisen, da die Anforderung noch nicht gestartet wurde. Sie verwenden im MINIT()-Schritt niemals Zend-Speicherverwaltungszuweisungen, es werden jedoch permanente Zuweisungen verwendet. Nicht emalloc(), sondern pemalloc(). Andernfalls kommt es zu einem Absturz.

In MINIT() ist die Ausführungs-Engine immer noch nicht gestartet. Versuchen Sie daher nicht, ohne besondere Aufmerksamkeit auf eine ihrer Strukturen zuzugreifen.

Wenn Sie einen INI-Eintrag für Ihre Erweiterung registrieren müssen, ist MINIT() der richtige Ansatz.

Wenn Sie schreibgeschützte zend_strings für die spätere Verwendung registrieren möchten, verwenden Sie die dauerhafte Zuordnung.

Wenn während der Verarbeitung einer Anfrage auf die Objekte geschrieben wird, die Sie zuweisen müssen, müssen Sie deren Speicherzuordnung in den threadspezifischen Pool für diese Anfrage kopieren. Denken Sie daran, dass Sie nur innerhalb von MINIT() sicher in den globalen Bereich schreiben können.

Hinweis

Speicherverwaltung, Zuweisung und Debugging sind Teil der Kapitel zur Speicherverwaltung.

Triggern Sie in der Funktion php_module_startup() zend_startup_modules() bis MINIT() aus.

Modulbeendigung: MSHUTDOWN()

Dies ist der PHP-Prozessbeendigungsschritt. Es ist ganz einfach, im Grunde führen Sie hier das Gegenteil von dem aus, was Sie in MINIT() verwendet haben. Sie geben Ressourcen frei, heben die Registrierung von INI-Einstellungen auf usw.

Noch einmal beachten: Die Ausführungs-Engine ist ausgeschaltet, daher sollten Sie hier auf keine ihrer Variablen zugreifen.

Da Sie hier keine Anfragen benötigen, sollten Sie zum Freigeben von Ressourcen nicht die efree() von Zend Memory Management oder ähnliche Funktionen verwenden, sondern für die Freigabe persistenter Zuordnungen pefree() verwenden.

In der Funktion php_module_shutdown() wird zend_shutdown() durch das zend_destroy_modules() von MSHUTDOWN() ausgelöst.

Anforderungsinitialisierung: RINIT()

Die Anforderung, die Sie sich gerade angesehen haben, PHP wird sie hier verarbeiten. In RINIT() weisen Sie die Ressourcen zu, die zur Bearbeitung dieser genauen Anfrage erforderlich sind. PHP ist eine Shared-Nothing-Architektur, die Speicherverwaltungsfunktionen bietet.

Wenn Sie in RINIT() dynamischen Speicher zuweisen müssen, verwenden Sie den Zend-Speichermanager. Sie werden emalloc() anrufen. Der Zend-Speichermanager verfolgt den Speicher, den Sie damit zuweisen, und wenn eine Anfrage geschlossen wird, versucht er, den an die Anfrage gebundenen Speicher freizugeben, wenn Sie dies vergessen (was Sie nicht tun sollten).

Hier sollten Sie keinen persistenten dynamischen Speicher anfordern, also libcs ​​malloc() oder Zends pemalloc(). Wenn Sie hier persistenten Speicher anfordern und vergessen, ihn freizugeben, erzeugen Sie ein Leck, das sich anhäuft, da PHP immer mehr Anfragen verarbeitet, was schließlich zum Absturz des Prozesses führt (Kernel OOM) und der Maschine nicht mehr genügend Speicher zur Verfügung steht.

Achten Sie außerdem darauf, hier nicht in den globalen Raum zu schreiben. Wenn PHP als ausgewähltes paralleles Modell in Threads ausgeführt wird, ändern Sie den Kontext in jedem Thread-Pool (alle Anforderungen werden parallel zu Ihrer Anforderung verarbeitet). Wenn Sie den Speicher nicht sperren, kann auch eine Race-Bedingung ausgelöst werden. Wenn Sie das große Ganze wollen, müssen Sie sie schützen.

Hinweis

Das globale Scope-Management wird in einem eigenen Kapitel erläutert.

Triggern Sie in der Funktion php_request_startup() zend_activate_module() bis RINIT() aus.

Beendigung der Anfrage: RSHUTDOWN()

Dies ist der Schritt zur Beendigung der PHP-Anfrage. PHP hat gerade die Verarbeitung seiner Anfragen abgeschlossen und jetzt ist es an der Zeit, seinen Teil des Speichers als Shared-Nothing-Architektur zu bereinigen. Nachfolgende Anfragen sollten sich an nichts von der aktuellen Anfrage erinnern. Es ist ganz einfach, im Grunde machen Sie das Gegenteil von dem, was RINIT() hier verwendet. Sie geben die durch die Anforderung gebundene Ressource frei.

Da Sie hier Anfragen verwenden, sollten Sie die Ressource mit efree() oder ähnlichem des Zend-Speichermanagers freigeben. Wenn Sie das Freigeben vergessen und ein Leck verursachen, protokolliert der Speichermanager bei Debug-Builds die durchgesickerten Zeiger im Prozessstderr und gibt sie für Sie frei.

Um Ihnen eine Vorstellung zu geben, wird RSHUTDOWN() wie folgt aufgerufen:

  • Nach dem Ausführen der Funktion zum Schließen des Benutzerbereichs (register_shutdown_function())
  • nach dem Aufrufen jedes Objekts Analyse Nach dem Konstruktor
  • Nachdem der PHP-Ausgabepuffer geleert wurde
  • Nach dem Deaktivieren von max_execution_time

In der Funktion php_request_shutdown() zend_deactivate_modules() über RSHUTDOWN() auslösen .

Beendigung der Post-Anfrage: PRSHUTDOWN()

Dieser Hook wird selten verwendet. Es wird nach RSHUTDOWN() aufgerufen, dazwischen wird jedoch zusätzlicher Engine-Code ausgeführt.
Besonders in Post-RSHUTDOWN:

  • PHP-Ausgabepuffer wurde geschlossen und sein Handler wurde geleert
  • PHP Superglobal wurde zerstört
  • Ausführungs-Engine wurde wurde abgeschaltet

Dieser Haken wird selten verwendet. In der Funktion php_request_shutdown() wird sie nach zend_post_deactivate_modules() bis RSHUTDOWN() ausgelöst.

Globale Initialisierung: GINIT()

Die Thread-Bibliothek ruft diesen Hook jedes Mal auf, wenn ein Thread geöffnet wird. Wenn Sie mehrere Prozesse verwenden, rufen Sie diese Funktion beim Start von PHP nur auf, bevor MINIT() ausgelöst wird.

Ich werde hier nicht auf zu viele Details eingehen, initialisieren Sie hier einfach die globalen Variablen, die normalerweise auf 0 initialisiert werden. Das globale Management wird in einem eigenen Kapitel ausführlich erläutert.

Denken Sie daran, dass globale Variablen nicht nach jeder Anfrage bereinigt werden. Wenn Sie sie (möglicherweise) für jede neue Anfrage zurücksetzen müssen, müssen Sie solche Prozesse in RINIT() einfügen.

Hinweis

Die globale Bereichsverwaltung wird im entsprechenden Kapitel ausführlich beschrieben.

Globale Beendigung: GSHUTDOWN()

In der Thread-Bibliothek wird dieser Hook immer dann aufgerufen, wenn ein Thread beendet wird. Wenn Sie Multithreading verwenden, wird diese Funktion einmal während der PHP-Beendigung aufgerufen (bei MSHUTDOWN()).

Ohne hier zu viele Details anzugeben, können Sie Ihre globalen Variablen hier einfach deinitialisieren. Normalerweise müssen Sie nichts tun, aber wenn beim Erstellen der globalen Variablen (GINIT()) Ressourcen zugewiesen werden, finden Sie sie hier die Schritte, die Sie unternehmen sollten, um sie zu befreien.

Globales Management wird in einem eigenen Kapitel ausführlich vorgestellt.

Denken Sie daran, dass globale Variablen nicht nach jeder Anfrage gelöscht werden. Das heißt, GSHUTDOWN() wird nicht als Teil von RSHUTDOWN() aufgerufen.

Hinweis

Das globale Scope-Management wird im entsprechenden Kapitel ausführlich vorgestellt.

Informationssammlung: MINFO()

Dieser Hook ist etwas Besonderes, er wird nie automatisch von der Engine ausgelöst, sondern nur, wenn Sie ihn nach Informationen über die Erweiterung fragen . Ein typisches Beispiel ist der Aufruf von phpinfo(). Diese Funktion wird dann ausgeführt und spezielle Informationen über die aktuelle Erweiterung werden in den Stream gedruckt.

Kurz gesagt: phpinfo() zeigt Informationen an.

Diese Funktion kann auch über die CLI mit einem der Reflection-Schalter aufgerufen werden, z. B. php --ri pib oder über Userland ini_get_all().

Sie können dieses Feld leer lassen. In diesem Fall wird nur der Name der Erweiterung und nichts anderes angezeigt (die INI-Einstellungen werden möglicherweise nicht angezeigt, da dies Teil von MINFO() ist).

Gedanken zum PHP-Lebenszyklus

Entdecken Sie den PHP-Lebenszyklus

Sie haben vielleicht herausgefunden, dass RINIT() und RSHUTDOWN() besonders wichtig sind, weil sie tausende Male in Erweiterungen ausgelöst werden . Wenn der PHP-Schritt für das Web (nicht CLI) bestimmt ist und für die Verarbeitung einer unendlichen Anzahl von Anfragen konfiguriert wurde, wird Ihre RINIT()/RSHUTDOWN()-Gruppe unendlich oft aufgerufen.

Wir möchten Sie noch einmal auf das Thema Speichermanagement aufmerksam machen. Bei der Verarbeitung von Anfragen (zwischen RINIT() und RSHUTDOWN()) gehen am Ende kleine Bytes verloren, die schwerwiegende Auswirkungen auf einen voll ausgelasteten Server haben. Aus diesem Grund wird empfohlen, für solche Zuweisungen den Zend Memory Manager zu verwenden und darauf vorbereitet zu sein, das Speicherlayout zu debuggen. Als Teil seiner Shared-Nothing-Architektur vergisst PHP den angeforderten Speicher und gibt ihn am Ende jeder Anfrage frei. Dies liegt am internen Design von PHP.

Außerdem stürzt der gesamte Prozess ab, wenn Ihr Absturzsignal SIGSEGV (schlechter Speicherzugriff) ist. Wenn PHP Threads als Multiprozess-Engine verwendet, stürzen auch alle anderen Threads ab, was möglicherweise sogar zum Absturz des Servers führt.

Hinweis

C-Sprache ist keine PHP-Sprache. Bei C führen Fehler im Programm wahrscheinlich dazu, dass das Programm abstürzt und beendet wird.

Hooking durch Überschreiben von Funktionszeigern

Da Sie nun wissen, wann die Engine Code auslöst, gibt es auch erwähnenswerte Funktionszeiger, die Sie ersetzen können, um sich in die Engine einzubinden. Da es sich bei diesen Zeigern um globale Variablen handelt, können Sie sie durch MINIT()-Schritte ersetzen und wieder in MSHUTDOWN() einfügen.

Von Interesse:

  • AST, Zend/zend_ast.h:

    • void (zend_ast_process_t) (zend_ast ast)
  • Compiler, Zend/zend_compile.h:

    • zend_op_array ( zend_compile_file)(zend_file_handle file_handle, int type)*
    • zend_op_array (zend_compile_string)(zval source_string, char filename)
  • Executor, Zend/zend_execute.h:

    • void (zend_execute_ex)(zend_execute_data execute_data)
    • void (zend_execute_internal)(zend_execute_data execute_data, zval return_value)*
  • GC, Zend/ zend_gc.h:

    • int (gc_collect_cycles)(void)*
  • TSRM, TSRM/TSRM h:

    • void (tsrm_thread_begin_func_t)(THREAD_T thread_id)*
    • void (tsrm_thread_end_func_t)(THREAD_T thread_id)*
  • Fehler, Zend/zend.h:

    • void (zend_error_cb)(int type, const char error_filename, const uint error_lineno, const char format, va_list args)*
  • Ausnahmen, Zend/zend_Exceptions.h:

    • void (zend_throw_Exception_hook)(zval ex)
  • Lifetime, Zend/zend.h:

    • void (zend_on_timeout)(int seconds)*
    • void (zend_interrupt_function)(zend_execute_data execute_data)
    • void (zend_ticks_function)(int ticks)*

Es gibt noch andere, aber die oben genannten sind die wichtigsten, wenn Sie entwerfen Möglicherweise benötigen Sie PHP-Erweiterungen. Da ihre Namen leicht lesbar sind, werden sie nicht im Detail erläutert.

Wenn Sie weitere Informationen benötigen, können Sie im PHP-Quellcode nachsehen, wann und wie diese ausgelöst werden.

Das obige ist der detaillierte Inhalt vonEntdecken Sie den PHP-Lebenszyklus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen