PHP及びZend Egineのスレッド安全モデル分析


どういうことかよく分かりませんが、ソースを読むことと限られた資料を調べることで、関連メカニズムを簡単に調べてみます。本論文ではまず、スレッドの安全の概念とPHPにおけるスレッドの安全の背景を説明し、PHPのスレッド安全機構ZTS(Zend Thread Safety)とTSRMの具体的な実現について詳細に検討した。研究内容は関連データ構造、詳細及び運転メカニズムを含み、最後にZendが単スレッドとマルチスレッド環境に対する選択的なコンパイル問題を検討した。スレッドセキュリティスレッドの安全問題は、一言で言えば、マルチスレッド環境の下で、どうやって公共資源に安全にアクセスするかです。私達は知っています。各スレッドはただ一つのプライベートスタックを持っていて、所属プロセスのヒープを共有しています。Cでは、変数がどの関数以外で宣言されてもグローバル変数になります。この変数は、プロセスの共有記憶空間に割り当てられます。異なるスレッドは同じアドレス空間を参照します。したがって、スレッドはこの変数を修正すると、スレッド全体に影響を及ぼします。これはスレッド共有データのための便利さを提供しているように見えるが、PHPはしばしばスレッドごとに要求を処理するので、要求間の干渉を望まずに、スレッドごとにグローバル変数のコピーを保有してほしい。初期のPHPは単スレッド環境によく使われ、各プロセスはスレッドのみが起動しますので、スレッドの安全問題は存在しません。その後、マルチスレッド環境でPHPを使用するシーンが発生したため、Zendはスレッドの安全を保証するために、Zendスレッドセキュリティ機構(Zend Thread Safety)を導入した。ZTSの基本原理と基本思想の実現は、ZTSの基本思想が直観的であり、各グローバル変数がスレッドごとにコピーを持つ必要があるのではないでしょうか?マルチスレッド環境において、グローバル変数を申請するのは、単に変数を宣言するのではなく、プロセス全体がメモリ空間の一部をヒープに割り当てて「スレッドグローバル変数プール」として使用し、プロセス起動時にこのメモリプールを初期化し、有線でグローバル変数を申請するたびに、TSRM(Thread Safe Resource Manager)を呼び出す仕組みを提供します。ZTSの具体的な実現)と必要なパラメータ(変数サイズなど)を伝え、TSRMはメモリプールにそれぞれのメモリブロックを割り当て、このメモリの参照標識を返します。このように、次回このスレッドがこの変数を読み書きする必要がある場合、唯一の参照識別子をTSRMに渡すことで、TSRMは本当に読み書き操作を担当します。これにより、スレッドの安全なグローバル変数が実現される。以下の図は、ZTSの原理を示す概略図である。image Thread 1とThread 2は同じプロセスである。ここでは、それぞれ一つのグローバル変数Global Varが必要であり、TSRMはスレッドグローバルメモリプール(黄色部分)にそれぞれ一つの領域を割り当て、一意のIDで識別することで、TSRMを介して自分の変数にアクセスして互いに干渉しない。以下は具体的なコードセグメントを通して、Zendが具体的にどのようにこのメカニズムを実現しているかを確認します。ここはPHP 5.3.8のソースを使っています。TSRMの実現コードはPHPソースの「TSRM」カタログの下にあります。データ構造TSRMで重要なデータ構造は二つあります。tlsentryとtsrm_レスリングタイプ。次にtsrmを見ますtlsentrytsrm_tlsentry定義はTSRM/TSRM.c中:
 
typedef struct _tsrm_tls_entry tsrm_tls_entry;

struct _tsrm_tls_entry {
void **storage;
int count;
THREAD_T thread_id;
tsrm_tls_entry *next;
}
各tsrm_tlsentry構造は、スレッドの全グローバル変数リソースを表します。idはスレッドIDを記憶し、countはグローバル変数数を記録し、nextは次のノードを指す。storgeは、ポインタ配列として考えられ、各要素は、ノードがスレッドを表すグローバル変数である。最終スレッドのtsrm_tlsentryはチェーン構造になっています。そして、グローバル静的変数tsrm_に対して、チェーンの先頭ポインタを割り当てます。tlsテーブル。注意、tsrm_のためですtlsテーブルは本物のグローバル変数ですので、すべてのスレッドがこの変数を共有します。これはスレッド間のメモリ管理の整合性を実現します。tsrm_tlsentryとtsrm_tlsテーブル構造の概略図は以下の通りである。image tsrm_レスリングtypeの内部構造は比較的簡単です。
 
typedef struct {
size_t size;
ts_allocate_ctor ctor;
ts_allocate_dtor dtor;
int done;
}
tsrm_レスリングタイプ前にtsrm_と言ったことがありますtlsentryはスレッド単位(スレッド毎のノード)であり、tsrm_レスリングtypeは資源(またはグローバル変数)単位で、新しいリソースが割り当てられるたびにtsm(u)を作成します。レスリングタイプ。すべてのtsrm_レスリングtypeは配列(線形表)でtsrm_を構成します。レスリングテーブルの下に表示されているのがこのリソースのIDです。各tsrm_レスリングtypeはこのリソースのサイズと構造、解析方法の指針を格納しています。ある程度、tsrm_レスリングテーブルはハッシュテーブルとして見られ、keyはリソースIDであり、valueはtsrm_である。レスリングtype構造詳細を実現するこの小節はTSRMのいくつかのアルゴリズムの実現の詳細を分析します。TSRM全体はコードに関連しているので、ここでは代表的な二つの関数を選んで分析します。一番目の注意点はtsrm_です。startup関数は、この関数がプロセスの開始段階でsapiによって呼び出され、TSRMの環境を初期化するために使用されます。tsrm_のためにstartupはやや長いですが、ここから注意すべき点を抜粋します。startupの主な任務は上述の2つのデータ構造を初期化することである。一番目の面白いのは前の二つのパラメータです。expected(u)threadsとexpected_レスポンス。この二つのパラメータはsapiによって伝えられ、予想されるスレッド数とリソース数を表し、tsrm_を見ることができます。startupはこの二つのパラメータに従って空間を予め割り当てます。したがってTSRMはまずexpected_を収容できるように割り当てられます。threadsスレッドとexpected_レスキューの資源です。それぞれのsapiのデフォルトを見て、sapiのソースコード(sapiディレクトリの下で)を見ることができます。簡単に見ました。imageは比較的によく使われているsapiがmod_のように見えます。php 5、php-fpmとcgiはいずれもスレッドと一つのリソースを事前に割り当てているので、メモリ空間を無駄にしたくないです。また、多くの場合PHPはシングルスレッド環境で実行します。ここではid_も見られます。count変数は、この変数はグローバル静的変数であり、その役割は自己増加によってリソースIDが生成され、この変数はここで0に初期化される。だから、TSRMは資源IDを生成する方式がとても簡単です。つまり整形変数の自己増加です。二つ目は詳しく分析しなければならないのがtsuです。allocate_ID、PHP拡張を作成した友達はこの関数に慣れていないはずです。この関数は…