何が起こっているのかわからないのはいつも不安なので、ソースコードを読み、限られた情報を参照して関連するメカニズムを簡単に理解しました。この記事は私の研究内容を要約したものです。 この記事では、まず PHP におけるスレッド セーフティの概念とスレッド セーフティの背景について説明し、次に PHP のスレッド セーフティ メカニズム ZTS (Zend Thread Safety) と TSRM の具体的な実装について詳細に調査します。研究内容には、関連するデータ構造が含まれます。実装の詳細と動作メカニズム 最後に、シングルスレッド環境とマルチスレッド環境における Zend の選択的コンパイルの問題について検討しました。
スレッド セーフティ
スレッド セーフティの問題は、一言で言えば、マルチスレッド環境でパブリック リソースに安全にアクセスする方法です。各スレッドはプライベート スタックのみを持ち、それが属するプロセスのヒープを共有することがわかっています。 C では、関数の外で変数を宣言すると、その変数はプロセスの共有記憶領域に割り当てられるため、スレッドが変更すると、その変数はプロセスの共有記憶領域に割り当てられます。この変数が設定されている場合、すべてのスレッドに影響します。これはスレッドがデータを共有するのに便利であるように見えますが、PHP はスレッドごとに 1 つのリクエストを処理することが多いため、各スレッドがグローバル変数のコピーを持つことが望まれ、リクエストが相互に干渉することは望ましくありません。 初期の PHP はシングルスレッド環境で使用されることが多く、各プロセスは 1 つのスレッドのみを開始するため、スレッドの安全性の問題はありませんでした。その後、マルチスレッド環境での PHP の使用が登場したため、Zend はスレッドの安全性を確保するために Zend Thread Safety (ZTS) メカニズムを導入しました。
ZTSの基本原理と実装
基本的な考え方
ZTSの基本的な考え方と言えば、非常に直感的ですが、各グローバル変数は各スレッドにコピーを持つ必要があるということではないでしょうか。次に、このメカニズムを提供します。マルチスレッド環境では、グローバル変数の適用は変数の単純な宣言ではなくなり、プロセス全体が「スレッド グローバル変数プール」として使用されるヒープ上のメモリ領域を割り当てます。 、プロセスの開始時に初期化されます。このメモリ プールでは、スレッドがグローバル変数を適用する必要があるたびに、対応するメソッドを通じて TSRM (Thread Safe Resource Manager、ZTS の特定の実装) を呼び出し、必要なパラメータを渡します ( TSRM は、次回スレッドがこの変数の読み取りまたは書き込みを行う必要があるように、対応するメモリ ブロックとこのメモリの参照 ID をメモリ プールに割り当てる役割を果たします。一意の参照 ID を TSRM に渡すと、TSRM が実際の読み取りおよび書き込み操作を担当します。これにより、スレッドセーフなグローバル変数が実現します。次の図は、ZTS 原理の概略図を示しています。
Thread1 と Thread2 は同じプロセスに属しており、それぞれにグローバル変数 Global Var が必要です。TSRM は、スレッド グローバル メモリ プール内でそれぞれに領域を割り当てます (黄色の部分)。 )、識別用の一意の ID を渡します。これにより、2 つのスレッドが互いに干渉することなく TSRM を介して独自の変数にアクセスできます。 Zend が特定のコード スニペットを通じてこのメカニズムをどのように実装しているかを見てみましょう。ここではPHP5.3.8のソースコードを使用しています。 TSRMの実装コードは、PHPソースコードの「TSRM」ディレクトリにあります。
データ構造
TSRM には、tsrm_tls_entry と tsrm_resource_type という 2 つの重要なデータ構造があります。まず tsrm_tls_entry を見てみましょう。 tsrm_tls_entry は TSRM/TSRM.c で定義されています。
typedef struct _tsrm_tls_entry tsrm_tls_entry;
** のストレージを無効にします。
THREAD_T thread_id;
tsrm_tls_entry *次へ
}
各 tsrm_tls_entry 構造体は、スレッドのすべてのグローバル変数リソースを表す役割を果たします。ここで、thread_id はスレッド ID を格納し、count はグローバル変数の数を記録し、next は次のノードを指します。ストレージはポインターの配列として見ることができます。各要素は、このノードによって表されるスレッドを指すグローバル変数です。最後に、各スレッドの tsrm_tls_entry がリンク リスト構造に形成され、リンク リストの先頭ポインタがグローバル静的変数 tsrm_tls_table に割り当てられます。 tsrm_tls_table は実際のグローバル変数であるため、すべてのスレッドがこの変数を共有し、スレッド間のメモリ管理の一貫性が実現されることに注意してください。 tsrm_tls_entry 構造と tsrm_tls_table 構造の概略図は次のとおりです。
tsrm_resource_type の内部構造は比較的単純です。
コードをコピー コードは次のとおりです。
typedef struct {
size_t size; ts_allocate_ctor ctor ;
ts_allocate_dtor dtor;
intned ;
}
tsrm_resource_type; 前述のように、tsrm_tls_entry はリソース (またはグローバル変数) に基づいています。が割り当てられると、tsrm_resource_type が作成されます。すべての tsrm_resource_type は配列 (線形テーブル) の形式で tsrm_resource_table を形成し、その添え字がこのリソースの ID になります。各 tsrm_resource_type には、このリソースのサイズ、構築および破棄メソッド ポインターが格納されます。 tsrm_resource_table はある程度、ハッシュ テーブルとみなすことができます。キーはリソース ID、値は tsrm_resource_type 構造体です。
実装の詳細
このセクションでは、一部の TSRM アルゴリズムの実装の詳細を分析します。 TSRM 全体には多くのコードが含まれるため、ここでは分析用の 2 つの代表的な関数を紹介します。 最初に注目すべき点は tsrm_startup 関数です。この関数は、TSRM 環境を初期化するプロセスの開始時に sapi によって呼び出されます。 tsrm_startup は少し長いので、注意すべき点の抜粋を示します。
コードをコピー コードは次のとおりです:
/* TSRM の起動 (プロセス全体で 1 回の呼び出し) * /
TSRM_API int tsrm_startup(int Expected_threads , int Expected_resources, int debug_level, char *debug_filename)
{
/* コード... */
tsrm_tls_table_size = Expected_threads = (tsrm_tls_エントリ **) calloc(tsrm_tls_table_size, sizeof (tsrm_tls _entry *));
if (!tsrm_tls_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "TLS テーブルを割り当てられません"));
id_count=0;ソース;
resource_types_table = (tsrm _resource_type *) calloc(resource_types_table_size, sizeof (tsrm_resource_type));
if (!resource_types_table) {
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "リソース タイプ テーブルを割り当てられません")); tls_table = NULL;
return 0;
/* コード... */
return 1;
実際、 tsrm_startup の主なタスクは、上記の 2 つのデータ構造を初期化することです。まず興味深いのは、最初の 2 つのパラメーター、expected_threads と Expected_resources です。これら 2 つのパラメータは、予想されるスレッドとリソースの数を示すために sapi によって渡されます。tsrm_startup がこれら 2 つのパラメータに従って (calloc を通じて) スペースを事前に割り当てることがわかります。したがって、TSRM は最初に、expected_threads スレッドと Expected_resources リソースを収容できるリソースを割り当てます。各 sapi がデフォルトで何を渡すかを確認するには、各 sapi のソース コード (sapi ディレクトリ内) を簡単に見てみましょう:
mod_php5、php など、より一般的に使用されていることがわかります。 -fpm と cgi はすべて、メモリ領域を無駄にしたくないため、1 つのスレッドと 1 つのリソースを事前に割り当てます。また、ほとんどの場合、PHP は依然としてシングルスレッド環境で実行されます。 ここで id_count 変数も確認できます。この変数は、自動インクリメントによってリソース ID を生成することを目的としています。したがって、TSRM がリソース ID を生成する方法は非常に簡単です。整数変数を自動インクリメントするだけです。 2 番目に注意深く分析する必要があるのは ts_allocate_id です。PHP 拡張機能を作成したことのある友人は、この関数に精通しているはずです。
http://www.bkjia.com/PHPjc/324589.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/324589.html技術記事何が起こっているのかわからないのはいつも不安なので、ソースコードを読み、限られた情報を参照して関連するメカニズムを簡単に理解しました。この記事は私の研究内容を要約したものです。 まずはこの記事から…