ホームページ  >  記事  >  バックエンド開発  >  PHP のライフサイクルを調べる

PHP のライフサイクルを調べる

coldplay.xixi
coldplay.xixi転載
2020-07-28 16:38:082045ブラウズ

PHP のライフサイクルを調べる

PHP ライフ サイクルを学ぶ

PHP のライフ サイクルは非常に複雑なプロセスであり、そのライフ サイクルは、使いこなしたい人が習得する必要があります。それ。主な内容は次のとおりです。

PHP の起動。 CLI または FPM を実行している場合は、C main() が実行されます。 apxs2 SAPI (Apache 2) のように、ネットワーク サーバーへのモジュールとして実行する場合、Apache の起動直後に PHP が起動し、そのモジュール (PHP もその 1 つ) の起動シーケンスの実行を開始します。スタートアップは内部的に Module Startup Step と呼ばれます。 MINIT ステップとも略します。

PHP が開始されると、1 つまたは複数のリクエストの処理を待機します。 PHP CLI について話すとき、リクエストは 1 つだけです。実行する現在のスクリプトです。しかし、Web 環境について話す場合、それは PHP-FPM または Web サーバー モジュールである必要があります。PHP は複数のリクエストを次々に処理できます。すべては Web サーバーの構成方法によって異なります。無制限の数のリクエストを処理するように指示することも、プロセスをシャットダウンしてリサイクルする前に特定の数のリクエストを処理するように指示することもできます。 PHP は、新しいリクエストがスレッドで処理されるたびに、リクエスト開始ステップを実行します。これを #RINIT と呼びます。

関連する学習の推奨事項: PHP プログラミングの入門から熟練度まで

リクエストが処理され、(おそらく) コンテンツが生成されました。リクエストを閉じて、別のリクエストを処理する準備をします。クローズ要求は、リクエストクローズステップを呼び出します。これを #RSHUTDOWN と呼びます。 ·

X リクエスト (1 つ、数十、数千など) を処理した後、PHP は最終的に自動的にシャットダウンして終了します。 PHP プロセスのシャットダウンは、モジュール シャットダウン ステップと呼ばれます。略称は MSHUTDOWN です。

これらの手順を描くことができれば、次のようになります:

PHP のライフサイクルを調べる

並列モデル

CLI 環境では、何でも簡単です。 : プロセスはリクエストを処理します。別の PHP スクリプトを開始して終了します。 CLI 環境は Web 環境を特殊化したものであり、より複雑です。

複数のリクエストを同時に処理するには、並列モデルを実行する必要があります。 PHP には 2 つのタイプがあります。

  • #プロセスベースのモデルプロセスベースのモデル
  • #スレッドベースのモデルスレッドベースのモデル

Use プロセス モデルに基づいて、オペレーティング システムは各 PHP インタープリターを独自のプロセスに分離します。このモデルは Unix では非常に一般的です。各リクエストは独自のプロセスに送られます。 PHP-CLI、PHP-FPM、および PHP-CGI はこのモデルを使用します。

スレッドベースのモデルでは、各 PHP インタープリターは、スレッド ライブラリを使用してスレッドに分離されます。このモデルは主に Windows オペレーティング システムで使用されますが、ほとんどの Unix でも使用できます。 PHP とその拡張機能を ZTS モードで構築する必要があります。

これはプロセスベースのモデルです:

PHP のライフサイクルを調べる

これはスレッドベースのモデルです:

PHP のライフサイクルを調べる

Note

拡張機能開発者にとって、PHP のマルチプロセス モジュールは選択肢にはなりません。それをサポートする必要があります。拡張機能がスレッド環境 (特に Windows 上) で実行できるようにする必要があり、そのようにプログラムする必要があります。

PHP 拡張フック

ご想像のとおり、PHP エンジンは複数のライフサイクル ポイントで拡張機能をトリガーします。これらを フック関数 と呼びます。拡張機能は、エンジンに登録するときに関数フックを宣言することで、特定のライフサイクル ポイントへの関心を宣言できます。
これらのフックは、PHP 拡張構造 (zend_module_entry 構造) を分析すると明確にわかります。

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;
};

次に、コードにどの種類のフックを記述する必要があるかを見てみましょう。

モジュールの初期化: MINIT()

これは、PHP プロセスの起動ステップです。拡張された MINIT() では、後続の各リクエストに必要な永続オブジェクトまたは情報をロードして割り当てます。それらのほとんどは読み取り専用オブジェクトとして割り当てられます。

MINIT() では、スレッドやプロセスがまだポップアップしていないため、保護なしでグローバル変数に完全にアクセスできます。また、リクエストがまだ開始されていないため、リクエストにバインドされたメモリを割り当てることはできません。 MINIT() ステップでは Zend のメモリ管理割り当てを使用することはありませんが、永続的な割り当てが使用されます。 emalloc() ではなく、pemalloc() です。そうしないとクラッシュが発生します。

MINIT() では、実行エンジンがまだ開始されていないため、特別な注意を払わずにその構造にアクセスしようとしないでください。

拡張機能の INI エントリを登録する必要がある場合は、MINIT() が正しいアプローチです。

後で使用するために読み取り専用の zend_string を登録したい場合は、永続的割り当てを使用してください。

割り当てる必要があるオブジェクトがリクエストの処理中に書き込まれる場合は、それらのメモリ割り当てをそのリクエストのスレッド固有のプールにコピーする必要があります。グローバル空間に安全に書き込むことができるのは、MINIT() のみであることに注意してください。

メモリ管理、割り当て、およびデバッグは、メモリ管理の章の一部です。

php_module_startup() 関数では、MINIT()zend_startup_modules() を通じてトリガーされます。

モジュール終了: MSHUTDOWN()

これは、PHP プロセスの終了ステップです。基本的には簡単です。ここでは、MINIT() で使用したものとは逆のことを実行します。リソースを解放したり、INI 設定の登録を解除したりします。

もう一度注意してください: 実行エンジンは閉じられているため、ここではその変数にアクセスしないでください。

ここではリクエストが必要ないため、リソースを解放するために Zend Memory Management の efree() または同様の関数を使用しないでください。ただし、永続的な割り当てを解放するには、pefree ( )

php_module_shutdown() 関数では、MSHUTDOWN()zend_destroy_modules()zend_shutdown() によってトリガーされます。

リクエストの初期化: RINIT()

先ほど確認したリクエストは、PHP がここで処理します。 #RINIT() では、その正確なリクエストを処理するために必要なリソースを指示します。 PHP は、メモリ管理機能を提供するシェアードナッシング アーキテクチャです。

RINIT() で、動的メモリを割り当てる必要がある場合は、Zend メモリ マネージャーを使用します。 emalloc() を呼び出します。 Zend メモリ マネージャーは、ユーザーが割り当てたメモリを追跡し、リクエストが閉じられたときに、これを忘れた場合はリクエストにバインドされたメモリを解放しようとします (これは行うべきではありません)。

ここでは、永続的な動的メモリ、つまり libc の malloc() や Zend の pemalloc() をリクエストしないでください。ここで永続メモリをリクエストし、それを解放するのを忘れると、PHP が処理するリクエストが増えるにつれてリークが蓄積し、最終的にはプロセスがクラッシュし (カーネル OOM)、マシンのメモリが不足します。

また、ここではグローバル空間に書き込まないように注意してください。選択した並列モデルとして PHP がスレッド内で実行される場合、各スレッド プールのコンテキスト (すべてのリクエストが並行して処理される) を変更することになり、メモリをロックしないと競合状態がトリガーされる可能性もあります。全体像を把握したいなら、彼らを守らなければなりません。

グローバル スコープ管理については、専用の章で説明します。

php_request_startup() 関数では、RINIT()zend_activate_module() によってトリガーされます。

リクエスト終了: RSHUTDOWN()

これは、PHP リクエスト終了ステップです。 PHP はリクエストの処理を終えたところです。今度は、シェアードナッシング アーキテクチャとしてメモリの一部をクリーンアップします。後続のリクエストでは、現在のリクエストの内容を何も覚えてはいけません。それは簡単です。基本的に、ここで RINIT() が使用していることと逆のことを行うことになります。リクエストによってバインドされているリソースを解放します。

ここではリクエストを使用しているため、Zend メモリ マネージャーの efree() などを使用してリソースを解放する必要があります。解放するのを忘れてリークが発生した場合、デバッグ ビルドでメモリ マネージャーがプロセス stderr 上のリークしたポインターをログに記録し、それらを解放します。

ユーザー領域のシャットダウン関数 (register_shutdown_function()##) を実行した後、RSHUTDOWN()

が呼び出されます。 #)
  • 各オブジェクト デストラクターを呼び出した後 #PHP 出力バッファーをフラッシュした後 #max_execution_time を無効にした後
  • #php_request_shutdown() 関数でトリガー
  • RSHUTDOWN()
  • から
  • zend_deactivate_modules()
  • まで。
ポストリクエスト終了: PRSHUTDOWN()

このフックはほとんど使用されません。これは #RSHUTDOWN() の後に呼び出されますが、追加のエンジン コードが途中で実行されます。 特に RSHUTDOWN 後:

PHP 出力バッファーが閉じられ、そのハンドラーがフラッシュされました

PHP スーパーグローバルが破棄されました
実行エンジンはシャットダウンされました

  • このフックはめったに使用されません。 php_request_shutdown() 関数では、
  • zend_post_deactivate_modules()
  • を介して、
  • RSHUTDOWN()
  • の後にトリガーされます。
  • グローバル初期化: GINIT()

    スレッド ライブラリは、スレッドがポップアップするたびにこのフックを呼び出します。複数のプロセスを使用する場合、PHP の起動時に、MINIT() がトリガーされる前にのみこの関数が呼び出されます。

    ここではあまり詳しくは説明しませんが、単にここでグローバル変数を初期化するだけです (通常は 0 に初期化されます)。グローバル管理については、専用の章で詳しく説明します。

    グローバル変数はリクエストごとにクリーンアップされないことに注意してください。 (おそらく) 新しいリクエストごとにそれらをリセットする必要がある場合は、そのようなプロセスを RINIT() に入れる必要があります。

    グローバル スコープ管理については、専用の章で詳しく紹介されています。

    グローバル終了: GSHUTDOWN()

    スレッド ライブラリでは、スレッドが終了するたびにこのフックが呼び出されます。マルチスレッドを使用する場合、この関数は PHP の終了時 (MSHUTDOWN()) に 1 回呼び出されます。

    ここではあまり詳細を説明せずに、ここでグローバル変数を単純に初期化解除できます。通常は何もする必要はありませんが、グローバル (GINIT()) を構築している場合は、リソースが割り当てられたら、このステップでリソースを解放する必要があります。

    グローバル経営については、別章で詳しく紹介します。

    グローバル変数は各リクエスト後にクリアされないことに注意してください。つまり、GSHUTDOWN()RSHUTDOWN() の一部として呼び出されません。

    グローバル スコープ管理については、専用の章で詳しく紹介されています。

    情報収集: MINFO()

    このフックは非常に特殊で、エンジンによって自動的にトリガーされることはありません。エンジンに情報を要求した場合にのみトリガーされます。拡大。典型的な例は、phpinfo() の呼び出しです。次に、この関数が実行され、現在の拡張に関する特別な情報がストリームに出力されます。

    つまり、phpinfo() は情報を表示します。

    この関数は、php --ri pib などのリフレクション スイッチの 1 つを使用する CLI 経由、またはユーザーランド呼び出し ini_get_all() 経由で呼び出すこともできます。

    これを空白のままにすることもできます。その場合、拡張機能の名前のみが表示され、他は何も表示されません (これは MINFO() の一部であるため、INI 設定は表示されない場合があります)。

    PHP ライフサイクルについて考える

    PHP のライフサイクルを調べる

    RINIT()RSHUTDOWN() は拡張機能内で何千回もトリガーされるため、特に重要です。 PHP ステップが Web (CLI ではなく) 用であり、無制限のリクエストを処理するように構成されている場合、RINIT()/RSHUTDOWN() グループは無制限に呼び出されます。

    メモリ管理にもう一度注目していただきたいと思います。リクエストの処理中 (RINIT()RSHUTDOWN() の間) に小さなバイトがリークすることになり、フル負荷のサーバーに深刻な影響を及ぼします。このため、このような割り当てには Zend Memory Manager を使用し、メモリ レイアウトをデバッグする準備をしておくことをお勧めします。シェアードナッシング アーキテクチャの一部として、PHP は各リクエストの終了時に要求されたメモリを忘れて解放しますが、これは PHP の内部設計によるものです。

    また、クラッシュ信号が SIGSEGV (不正なメモリ アクセス) の場合、プロセス全体がクラッシュします。 PHP がマルチプロセス エンジンとしてスレッドを使用している場合、他のすべてのスレッドもクラッシュし、サーバーがクラッシュする可能性もあります。

    注意

    C 言語は PHP 言語ではありません。 C では、プログラム内のエラーによりプログラムがクラッシュして終了する可能性があります。

    関数ポインターのオーバーライドによるフック

    エンジンがコードを起動するタイミングがわかったので、エンジンにフックするために置き換えることができる注目に値する関数ポインターもあります。これらのポインターはグローバル変数であるため、MINIT() ステップに置き換えて、MSHUTDOWN() ステップに戻すことができます。

    興味深いものは次のとおりです:

    • ##AST, Zend/zend_ast.h:

        ##void (
      • zend_ast_process_t) (zend_ast ast)
      ##コンパイラ、Zend/zend_compile.h:
    • zend_op_array
        (
      • zend_compile_file )(zend_file_handle file_handle, int 型)*zend_op_array
      • (
      • zend_compile_string)(zval source_string, char filename)
      エグゼキュータ、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)*
      • エラー、Zend/zend.h:
    • void (

      zend_error_cb)(int 型、const char

      error_filename、const uint error_lineno、const char
        format、va_list args)*
      • Exceptions、Zend/zend_Exceptions.h:
        • void (zend_throw_Exception_hook)(zval ex)
    • ライフタイム、Zend/zend.h:

      • void (zend_on_timeout)(int 秒)*
      • void (zend_interrupt_function)(zend_execute_data execute_data)
      • void (zend_ticks_function)(inticks)*

    他にもありますが、上記は PHP を設計するときに最も重要です。拡張機能が必要になる場合があります。読みやすい名前ですので、詳しい説明は省略します。

    さらに詳しい情報が必要な場合は、PHP ソース コードを調べて、いつどのようにトリガーされるかを確認できます。

以上がPHP のライフサイクルを調べるの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。