ホームページ >バックエンド開発 >PHPチュートリアル >PHP カーネルの学習 - PHP ライフサイクル

PHP カーネルの学習 - PHP ライフサイクル

WBOY
WBOYオリジナル
2016-08-08 09:23:501061ブラウズ

すべての始まり: SAPI インターフェイス

SAPI (サーバー アプリケーション プログラミング インターフェイス) は、PC と同様に、PC インターフェイス仕様を満たしている限り、どのオペレーティング システムがインストールされているかに関係なく、特定の PHP アプリケーション用のプログラミング インターフェイスを指します。 PC 上では通常どおり動作します。PHP スクリプトは、Web サーバー経由、コマンド ラインから直接、または他のプログラムに埋め込むなど、さまざまな方法で実行できます。

通常、PHP スクリプトをテストするには、Apache や Nginx などの Web サーバーを使用するか、コマンドラインで PHP インタープリター プログラムを通じてスクリプトを実行します。 スクリプトが実行されると、Web サーバーが応答し、ブラウザーは応答情報を表示するか、コマンド ラインの標準出力に内容を表示します。

PHP インタープリターがどこにあるかを気にすることはほとんどありません。 Web サーバーとコマンドライン プログラムを介したスクリプトの実行は非常に異なって見えますが、ワークフローは実際には同じです。コマンド ライン パラメーターは、PHP インタープリターによって実行されるスクリプトに渡されます。これは、URL を介して PHP ページをリクエストするのと同じです。スクリプト実行後は応答結果が返されますが、ターミナル上にはコマンドラインからの応答結果が表示されます。

スクリプトの実行は、SAPI インターフェースの実装から始まります。たとえば、Apache の mod_php SAPI 実装は、Apache から取得した一部の情報を初期化し、コンテンツを出力するときにそのコンテンツを Apache に返す必要があるというだけです。

次のセクションでは、いくつかの一般的な SAPI 実装について詳しく説明します。

開始と終了

PHP が実行を開始すると、リクエストを処理する前の開始フェーズとリクエスト後の終了フェーズの 2 つの主なフェーズを経ます。 開始フェーズには 2 つのプロセスがあります。最初のプロセスはモジュール初期化フェーズ (MINIT) です。このプロセスは、SAPI ライフ サイクル全体 (Apache の開始後のライフ サイクル全体、または Apache の実行プロセス全体など) で 1 回だけ実行されます。コマンドラインプログラム)。 2 番目のプロセスはモジュール アクティベーション フェーズ (RINIT) で、リクエスト フェーズで発生します。たとえば、URL を通じてページがリクエストされた場合、各リクエストの前にモジュール アクティベーション (RINIT リクエストの開始) が実行されます。 たとえば、PHP がいくつかの拡張モジュールを登録すると、MINIT ステージ中にすべてのモジュールの MINIT 関数がコールバックされます。 モジュールはこの段階で、定数の登録、モジュールで使用されるクラスの定義などの初期化作業を実行できます。 モジュールが実装されると、これらのコールバック関数は次のマクロを通じて実装できます:

<span>PHP_MINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 注册常量或者类等初始化操作</span><span>return</span><span> SUCCESS; 
}</span>

リクエストが到着した後、PHP は、保存するシンボル テーブルを含む実行環境の作成など、スクリプトを実行するための基本環境を初期化します。 PHP 実行中の変数名と値の内容、および現在のすべての関数、クラス、その他の情報のシンボル テーブル。次に、PHP はすべてのモジュールの RINIT 関数を呼び出します。この段階で、モジュールの RINIT 関数は、MINIT コールバック関数と似ています:

<span>PHP_RINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求开始时间
    </span><span>//</span><span> 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了</span><span>return</span><span> SUCCESS; 
}</span>

リクエストが処理された後、一般的なスクリプト PHP は、最後まで実行されるか、exit() または die() 関数を呼び出すと、終了フェーズに入ります。開始フェーズに対応して、終了フェーズも 2 つのステージに分かれています。1 つはリクエストの完了後にモジュールを非アクティブ化するもの (RSHUTDOWN、RINIT に対応)、もう 1 つは SAPI ライフサイクルの終了時にモジュールを閉じるものです ( Web サーバーが終了するか、コマンド ライン スクリプトが実行されて終了します) (MSHUTDOWN、MINIT に対応)。

<span>PHP_RSHUTDOWN_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求结束时间,并把相应的信息写入到日至文件中。</span><span>return</span><span> SUCCESS; 
}</span>

シングルプロセス SAPI ライフサイクル

CLI/CGI モード PHP はシングルプロセス SAPI モードに属します。このタイプのリクエストは、リクエストを一度処理した後に閉じられます。つまり、次のリンクのみを経由します。 開始 - リクエストの開始 - リクエストのクローズ - 終了 SAPI インターフェイスの実装はライフ サイクルを完了します。以下の図に示すように:

単一プロセス SAPI ライフサイクル

上の図は非常にシンプルで理解しやすいです。ただ、PHP はさまざまな段階の間でも多くの作業を実行します。以下にいくつかの追加があります:

スタートアップ

各モジュールのモジュール初期化を呼び出す前に、以下を含む初期化プロセスがあります:

  • いくつかのグローバル変数を初期化する

ここで初期化されたグローバル変数は、ほとんどの場合次のようになります。 zuf (zend_utility_functions) の設定などの一部を除いて、NULL に設定します。 zuf.printf_function = php_printf を例にとると、ここでの php_printf は zend_startup 関数の zend_printf にグローバル関数ポインターとして割り当てられ、zend_printf 関数は通常は次のように使用されます。たとえば、プログラムの呼び出し履歴を表示する debug_print_backtrace は、関連情報を出力するために使用されます。

  • いくつかの定数を初期化します

ここでの定数は、PHP 独自の定数の一部です。これらの定数は、PHP_VERSION などのプログラム内にハードコーディングされるか、PEAR_EXTENSION_DIR などの構成ヘッダー ファイルに書き込まれます。 w32.h ファイル内にあります。

  • 初始化Zend引擎和核心组件

前面提到的zend_startup函数的作用就是初始化Zend引擎,这里的初始化操作包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。

  • 解析php.ini

php_init_config函数的作用是读取php.ini文件,设置配置参数,加载zend扩展并注册PHP扩展函数。此函数分为如下几步: 初始化参数配置表,调用当前模式下的ini初始化配置,比如CLI模式下,会做如下初始化:

INI_DEFAULT(<span>"</span><span>report_zend_debug</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>);
INI_DEFAULT(</span><span>"</span><span>display_errors</span><span>"</span>, <span>"</span><span>1</span><span>"</span>);

不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:

  1. 判断是否有php_ini_path_override,在CLI模式下可以通过-c参数指定此路径(在php的命令参数中-c表示在指定的路径中查找ini文件)。
  2. 如果没有php_ini_path_override,判断php_ini_ignore是否为非空(忽略php.ini配置,这里也就CLI模式下有用,使用-n参数)。
  3. 如果不忽略ini配置,则开始处理php_ini_search_path(查找ini文件的路径),这些路径包括CWD(当前路径,不过这种不适用CLI模式)、 执行脚本所在目录、环境变量PATH和PHPRC和配置文件中的PHP_CONFIG_FILE_PATH的值。
  4. 在准备完查找路径后,PHP会判断现在的ini路径(php_ini_file_name)是否为文件和是否可打开。 如果这里ini路径是文件并且可打开,则会使用此文件, 也就是CLI模式下通过-c参数指定的ini文件的优先级是最高的, 其次是PHPRC指定的文件,第三是在搜索路径中查找php-%sapi-module-name%.ini文件(如CLI模式下应该是查找php-cli.ini文件), 最后才是搜索路径中查找php.ini文件。
  • 全局操作函数的初始化

php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量,如:$_GET、$_POST、$_FILES等。 这里只是初始化,所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。

php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数, 这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。

  • 初始化静态构建的模块和共享模块(MINIT)

php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块(/ext/standard/目录, 这里是我们用的最频繁的函数,比如字符串函数,数学函数,数组操作函数等等),日历扩展模块、FTP扩展模块、 session扩展模块等。这些内置模块并不是一成不变的,在不同的PHP模板中,由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化, 比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。

模块初始化会执行两个操作: 1. 将这些模块注册到已注册模块列表(module_registry),如果注册的模块已经注册过了,PHP会报Module XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表( CG(function_table) ),如果函数无法添加,则会报 Unable to register functions, unable to load。

在注册了静态构建的模块后,PHP会注册附加的模块,不同的模式下可以加载不同的模块集,比如在CLI模式下是没有这些附加的模块的。

在内置模块和附加模块后,接下来是注册通过共享对象(比如DLL)和php.ini文件灵活配置的扩展。

在所有的模块都注册后,PHP会马上执行模块初始化操作(zend_startup_modules)。 它的整个过程就是依次遍历每个模块,调用每个模块的模块初始化函数, 也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。

  • 禁用函数和类

php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。 其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。

php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。 其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。

ACTIVATION

ファイル関連のコンテンツを処理した後、PHP は php_request_startup を呼び出してリクエストの初期化操作を実行します。 リクエストの初期化操作は、図に示されている各モジュールのリクエスト初期化関数の呼び出しに加えて、その他の多くの作業も実行します。

  • Zend エンジンをアクティブにする

gc_reset 関数を使用します。ガベージ コレクション メカニズムをリセットします。もちろん、これは PHP5.3 以降でのみ利用可能です。

init_compiler 関数は、コンパイルプロセス中にオペコードが配置される配列をクリアしたり、コンパイルに使用されるデータ構造を準備したりするなど、コンパイラーを初期化するために使用されます。

init_executor関数は中間コード実行プロセスの初期化に使用されます。 コンパイル プロセス中、関数リスト、クラス リストなどがコンパイル時にグローバル変数に保存され、実行プロセスの準備時に、これらのリストは次のように実行されるグローバル変数に割り当てられます。 function_table) ; 中間コードは PHP の実行仮想スタックで実行され、これらのスタックは初期化中に一緒に初期化されます。 スタックに加えて、変数を格納するシンボルテーブル(EG(symbol_table))は50要素のハッシュテーブルに初期化され、オブジェクトを格納するEG(objects_store)は1024要素で初期化されます。上記の変数の一部に加えて、PHP の実行環境にはエラー処理、例外処理などもあり、これらはすべてここで初期化されます。 php.ini で設定された zend_extensions も調べられ、ここで activate 関数が呼び出されます。

  • SAPI をアクティブにする

sapi_activate 関数は、SG (sapi_headers) と SG (request_info) を初期化し、HTTP リクエスト メソッドの内容を設定するために使用されます。たとえば、リクエスト メソッドが HEAD の場合、SG (request_info).headers_only を設定します。 =1 ; この関数の最も重要な操作は、要求されたデータを処理することであり、最終的に sapi_module.default_post_reader を呼び出します。 sapi_module.default_post_reader は、前のモジュールの初期化で php_startup_sapi_content_types 関数を通じて登録されました。デフォルトの処理関数は、main/php_content_types.c ファイルの php_default_post_reader 関数です。 この関数は、POST の生データを $HTTP_RAW_POST_DATA 変数に書き込みます。

投稿データを処理した後、PHP は sapi_module.read_cookies を通じて Cookie 値を読み取ります。CLI モードでは、この関数の実装は sapi_cli_read_cookies ですが、現在のモードの場合、関数本体には NULL が 1 つだけ返されます。 activate 関数が設定されている場合は、この関数を実行して SAPI をアクティブにします。CLI モードでは、この関数ポインタは NULL に設定されます。

環境の初期化
  • ここでの環境の初期化とは、ユーザー空間で使用する必要があるいくつかの環境変数の初期化を指します。ここでの環境には、サーバー環境、リクエストデータ環境などが含まれます。 実際に使用する変数は、$_POST、$_GET、$_COOKIE、$_SERVER、$_ENV、$_FILES です。 sapi_module.default_post_reader と同様に、sapi_module.treat_data の値も、モジュールの初期化中に php_startup_sapi_content_types 関数を通じて登録されます。デフォルトのデータ処理関数は、main/php_variables.c ファイル内の php_default_treat_data 関数です。

$_COOKIE を例にとると、php_default_treat_data 関数は区切り文字に基づいてすべての Cookie を分割し、対応する変数に割り当てます。

モジュールリクエストの初期化
  • PHP は、zend_activate_modules 関数を通じてモジュールリクエストの初期化を実装します。つまり、図で各拡張機能の RINIT を呼び出すことがわかります。 この関数は、 module_registry 変数に登録されているすべてのモジュールを走査し、その RINIT メソッドを呼び出すことによって、モジュールの要求初期化操作を実装します。

Run

php_execute_script 関数には、PHP スクリプトを実行するプロセス全体が含まれています。

PHP ファイルを解析して実行する必要がある場合、実行前ファイル、現在実行する必要があるメイン ファイル、実行後ファイルを含む 3 つのファイルを実行する必要がある場合があります。 2 つの非現行ファイルは、auto_prepend_file パラメーターと auto_append_file パラメーターを使用して php.ini ファイルに設定できます。 これら 2 つのパラメータが空に設定されている場合、対応する実行可能ファイルは無効になります。

解析して実行する必要があるファイルの場合、字句解析、構文解析、中間コード生成操作が zend_compile_file (compile_file 関数) によって実行され、このファイルのすべての中間コードが返されます。 解析されたファイルが有効な中間コードを生成した場合は、zend_execute (実行関数) を呼び出して中間コードを実行します。 実行中に例外が発生し、ユーザーがこれらの例外の処理を定義している場合、これらの例外処理関数が呼び出されます。 すべての操作が処理された後、PHP は EG (return_value_ptr_ptr) を通じて結果を返します。

DEACTIVATION

PHP がリクエストを閉じるプロセスは、いくつかの終了操作のセットであり、このセットは php_request_shutdown 関数内に存在します。 このコレクションには次のものが含まれます:

  1. register_shutdown_function() を通じて登録されたすべての関数を呼び出します。シャットダウン時に呼び出されるこれらの関数はユーザー空間に追加されました。 簡単な例として、スクリプト エラーが発生したときに統合関数を呼び出して、ユーザーにわかりやすいページを提供できます。これは Web の 404 ページに似ています。
  2. 利用可能なすべての __destruct 関数を実行します。 ここでのデストラクタには、オブジェクト プール内のすべてのオブジェクトのデストラクタ (EG (objects_store)) と EG 内の各要素のデストラクタ メソッド (symbol_table) が含まれます。
  3. すべての出力をフラッシュする これもプロセスです。文字列を出力しますが、この文字列は特定の仕様に準拠している可能性があります。
  4. 各モジュールのシャットダウン要求メソッドを実行し、各拡張機能の RSHUTDOWN を呼び出します。グローバル変数テーブル (PG (http_globals))
  5. zend_deactivate 関数を通じてレクサー、パーサー、および中間コード エグゼキューターをオフにします
  6. 基本的に、各拡張機能の post_deactivate_func 関数ポインターは NULL です。 SAPI、SG (sapi_headers)、SG (request_info) などの内容を破棄します。
  7. ストリーム ラッパーを閉じ、メモリ管理を閉じます。
  8. 最後に、
  9. flush
  10. sapi_flush は、CLI モードでは fflush 関数と同等です
zend_shutdown は Zend エンジンをシャットダウンします。今回は、各モジュールのシャットダウン モジュール操作を実行する必要があります。ここでは zend_hash_graceful_reverse_destroy 関数が 1 つだけあります。もちろん、モジュールを閉じるメソッドが呼び出されます。これは、ZEND_MODULE_DTOR マクロが設定されているためです。 module_registry が初期化され、ZEND_MODULE_DTOR マクロが module_destructor 関数に対応すると、モジュールの module_shutdown_func メソッドが呼び出されます。これは、PHP_RSHUTDOWN_FUNCTION マクロによって生成される関数です。

すべてのモジュールを閉じた後も、PHP はグローバル関数を破棄し続けます。テーブル、グローバル クラス テーブル、セールス グローバル変数テーブルなどを破棄します。これは、zend_shutdown_extensions を介して zend_extensions のすべての要素を横断します。

マルチプロセス SAPI ライフ サイクル
  • 通常、PHP は のモジュールとしてコンパイルされます。 apache は PHP リクエストを処理します。 Apache は通常、マルチプロセス モードを採用しており、Apache が起動されると、各プロセスは独立したメモリ領域を持ちます。ただし、各プロセスの開始段階のみが発生します。プロセスがフォークした後、プロセスの存続期間中に複数のリクエストが処理される可能性があります。 シャットダウン フェーズは、Apache がシャットダウンされるかプロセスが終了した後にのみ発生し、これら 2 つのフェーズの間で、リクエストの開始とリクエストのシャットダウン フェーズがリクエストごとに繰り返されます。 以下の図に示すように:

  • マルチプロセス SAPI ライフ サイクル

マルチスレッド SAPI ライフ サイクル

マルチスレッド モードは、マルチプロセスの特定のプロセスに似ていますが、次の点が異なります。プロセス全体のライフサイクル中、リクエストの開始からリクエストの終了までのプロセスが並行して繰り返されます

マルチスレッドSAPIライフサイクル

抜粋: http://www.php-internals.com/book /?p=chapt02/02-01- php-life-cycle-and-zend-engine

上記では、PHP カーネルと PHP のライフサイクルの学習について、内容の側面も含めて紹介しています。PHP チュートリアルに興味のある友人に役立つことを願っています。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
前の記事:ajax3—php(29)次の記事:ajax3—php(29)