【翻訳】PHPコア - 列管理

  • (文字列の管理:zend_string翻訳)

文字列の管理:zend_string

任意のプログラムは、文字列を管理する必要があります。ここでは詳細ますPHP用のカスタムソリューションを必要としますzend_stringPHP処理は、文字列がzend_string構造を使用するたびに必要。この構造は、C言語でchar *の包装の種類の結果。

それはそれをコピーすることなく、複数の場所で共有することができる文字列で、メモリ管理機能を高め、そう。さらに、いくつかの文字列は「抑留」され、メモリマネージャ特別な管理によって割り当てられた持続性メモリである、これは、複数の要求の間で多重化することによって達成することができます。それに続く文字列では、これらのだろうZendのメモリマネージャの永続的配分。

構造とアクセスのマクロ

ここでは単に表示するzend_string構造を:

struct _zend_string {
        zend_refcounted_h gc;
        zend_ulong        h;
        size_t            len;
        char              val[1];
};

あなたが見ることができるように、この構造が中に埋め込まれているzend_refcounted_hヘッド。これは、メモリ管理と参照カウントに行われます。文字列がキーを検出するために、ハッシュ・テーブルとして使用される可能性があるので、それはハッシュ値埋め込まれているので、h部材。これは、符号なしですzend_ulongこの番号はのみされzend_stringたハッシュを計算するために必要な場合には特にして、使用ハッシュテーブル:zend_array一緒に使用し、これは非常に可能性があります。

ご存知のように、文字列の長さは、それ自身を認識しているlenフィールド値、それによって「バイナリ文字列」をサポートすることができます。バイナリ文字列は、一つ以上含む文字列であるNUL文字(\ 0)。機能をlibcに渡されると、これらの文字列は切り捨てられ、またはその長さは、正しい方法を計算しません。すると、中にzend_string文字列の長さは、それが知られています。計算はASCII文字の長さを計算することである、ということに注意してください、最後に含まれていないNULが、中間の番号が含まNUL例えば、文字列「FOO」zend_string「FOO \ 0」の記憶は、その長さは3です。さらに、文字列"FOOの\の0barは" "FOOの\の0barの\ 0"、長さ7として記憶されます。

最後に、文字がに保存されているchar[1]フィールド。それはないがchar *、しかしchar[1]なぜ?これは、「C構造体ハック」(あなたはこの用語を検索するために行くことができます)と呼ばれるメモリの最適化、です。基本的に、それは、Zendエンジンを可能にするzend_string構造の種類と文字割り当てられたメモリ空間のみCポインタを使用して、格納されます。これは、(のためにかなりのメモリ内の2つのブロックを分配するよりも、ここでは連続したブロックとして割り当てられたメモリアクセス、メモリ最適化zend_string *のために、char *)。

C理由で、この構造体のハックを忘れないようにしてくださいzend_stringデバッグ(デバッグ文字列)へのCデバッガを使用する場合char型のCのメモリレイアウトと構造はこれです見る/あなたが感じることができる非常によく似ています。このハックを使用すると、対応するAPIオペレーションによって完全に管理することができますzend_string構造。

zend_string APIを使用します

最も単純な使用シナリオ

同様にZvals同じ、手動で操作する必要はありませんzend_stringフィールド内が、これを達成するために、マクロを使用しています。文字列に対してアクションをトリガーするためにいくつかのマクロが残っています。これらは関数ではありませんが、マクロはで定義されているのZend / zend_string.hヘッダファイル:

zend_string *str;

str = zend_string_init("foo", strlen("foo"), 0);
php_printf("This is my string: %s\n", ZSTR_VAL(str));
php_printf("It is %zd char long\n", ZSTR_LEN(str));

zend_string_release(str);

簡単な例は、上記の基本的な文字列の管理を示しています。関数は、zend_string_init()(それが実際にはマクロですが、私たちは、詳細な引数を渡すことができます)あなたに似復帰与えるべきchar *C列型,,だけでなく、その長さを。最後のパラメータはint型である-値が0または1でなければなりません。0が渡された場合、エンジンはメモリを割り当てる「の要求が結合」Zendのヒープメモリ割り当てマネージャを使用する必要があります。この割り当ては、現在のリクエストの終了時に破棄されます。あなたはデバッグビルドで、破壊しない場合、エンジンはあなたを教えてくれます:あなたの作成したメモリが漏れました。あなたがいずれかを使用する場合は、私たちが「持続」と呼ぶものを使用しますが割り当てられ、そのエンジンは、従来のCの使用するmalloc()コールをし、割り当てられたメモリを追跡しません。

  • あなたは、メモリ管理の詳細を知る必要がある場合、あなたは読むことができ、詳細な章を

その後、私はそれが文字列を表示メートル。私たちは、使うZSTR_VAL()文字の配列にアクセスするためのマクロを。ZSTR_LEN()アクセス文字列の長さの情報を許可します。zend_stringマクロはに関連しているZSTR_**()とのことに注意して、マクロの先頭Z_STR**()マクロではないと同じ!

  • 使用した長さsize_tストレージのタイプを。したがって、それは使用することができることを示すためにprintf()、「%ZD」形式で。そうでない場合、それがクラッシュしたり、セキュリティ上の問題を作成するためのアプリケーションを引き起こす可能性があります。printf()詳細形式の印刷、あなたが訪問することができ、このリンクを

最後に、我々が使用zend_string_release()する文字列を解放します。このバージョンは必須です。これは、メモリ管理に関するセクションです。「リリース」簡単な操作である:それがゼロの場合、APIは、あなたの記憶の文字列を解放します、文字列の参照カウントを減らします。あなたは、文字列の解放を忘れてしまった場合、メモリリークが発生する可能性があります。

  • あなたは、常にCとメモリ管理を考慮しなければなりません 直接使用-あなたはメモリの割り当て作成した場合malloc()、または割り当てられたAPIの使用を-あなたは、いくつかの点で使用する必要がありますfree()これが不可能な場合は、メモリリークが発生します、悪いようなプログラムを安全に使用することはできませんプログラムです。

ハッシュを使用します

あなたは、文字列のハッシュ値にアクセスする必要がある場合、使用ZSTR_H()しかし、あなたが作成しzend_string、自動的に対応するハッシュ値を計算していないとき。文字列がハッシュテーブルAPIと組み合わせて使用される場合、ハッシュ値が計算されます。あなたが計算されたハッシュ値を強制する場合は、使用しZSTR_HASH()たりzend_string_hash_val()ハッシュ値が計算されると、それが保存され、そして計算決してされます。何らかの理由で再計算をする必要がある場合-たとえば、あなたが文字列の値を変更するには、この時間を使用することができzend_string_forget_hash_val()、ハッシュ値を再計算します:

zend_string *str;

str = zend_string_init("foo", strlen("foo"), 0);
php_printf("This is my string: %s\n", ZSTR_VAL(str));
php_printf("It is %zd char long\n", ZSTR_LEN(str));

zend_string_hash_val(str);
php_printf("The string hash is %lu\n", ZSTR_H(str));

zend_string_forget_hash_val(str);
php_printf("The string hash is now cleared back to 0!");

zend_string_release(str);

文字列とメモリ管理をコピーします。

zend_stringAPIは非常に便利な機能を持っていることは、それは小さな文字列セクションで、簡単な宣言を「所有」することを可能にするということです。その後エンジンは、メモリ内の文字列をコピーしませんが、単に参照を(カウント増加zend_refcounted_hの一部)。これは、コードの多くの部分でメモリを共有することができます。

私たちは、この「コピー」の話をするたびにzend_string、時間、実際には、我々は、メモリの内容のいずれかを複製することはできません。あなたがする必要がある場合-実際にこれを行うことができます-のは、文字列のコピーについて話しましょう。起動します。

zend_string *foo, *bar, *bar2, *baz;

foo = zend_string_init("foo", strlen("foo"), 0); /* creates the "foo" string in foo */
bar = zend_string_init("bar", strlen("bar"), 0); /* creates the "bar" string in bar */

/* 创建 bar2 并将 bar 中的 "bar" 字符串 共享给 bar2
而且增加 "bar" 字符串的引用计数为 2*/
bar2 = zend_string_copy(bar);

php_printf("We just copied two strings\n");
php_printf("See : bar content : %s, bar2 content : %s\n", ZSTR_VAL(bar), ZSTR_VAL(bar2));

/* 在内存中复制 "bar" 字符串,创建了 baz 变量并使它是一个独立的 "bar" 字符串 */
baz = zend_string_dup(bar, 0);

php_printf("We just duplicated 'bar' in 'baz'\n");
php_printf("Now we are free to change 'baz' without fearing to change 'bar'\n");

/* 更改第二个 "bar" 字符串的最后一个字符,变成 "baz" */
ZSTR_VAL(baz)[ZSTR_LEN(baz) - 1] = 'z';

/* 当字符串值被改变,丢弃旧值的 hash(如果已经计算了),这样需要重新计算它的 hash */
zend_string_forget_hash_val(baz);

php_printf("'baz' content is now %s\n", ZSTR_VAL(baz));

zend_string_release(foo);  /* destroys (frees) the "foo" string */
zend_string_release(bar);  /* decrements the refcount of the "bar" string to one */
zend_string_release(bar2); /* destroys (frees) the "bar" string both in bar and bar2 vars */
zend_string_release(baz);  /* destroys (frees) the "baz" string */

私たちは、ディストリビューション「foo」と「bar」から始まります。その後、我々は、作成bar2などの文字列をbarコピー。ここでは、我々はすべて覚えておく必要がありますbarし、bar2同じメモリ領域Cの文字列をポイントし、変更は、他に変更されます。これは、あるzend_string_copy()行動:それはちょうどC文字列が参照カウントを持って追加されます。

私たちは、メモリ内の文字列の二つの別々のコピーを持ってしたいこと- -私たちはこの文字列を分離したい場合は、私たちが使用する必要がありますzend_string_dup()コピーを。私たちは、その後、bar2文字列の中に変数をコピーしたbaz変数。さて、baz変数は文字列の独自のコピーに埋め込まれ、かつ影響を与えることはできませんbar2その場合の変化を。それは我々が何をすべきかです:私たち「バー」最後の「r」は「Z」に置き換えます。その後、我々はそれを表示し、すべての文字列のメモリを解放します。

(前に計算されている場合、この詳細を検討する必要はありません)私たちは、ハッシュ値を忘れることに注意してください。それは良い習慣ことを覚えておく価値があります。前述のようにすれば、zend_stringハッシュテーブルの一部として使用される、あなたはハッシュを使用することができます。この開発はまた、ハッシュ値を再計算する必要がある文字列の値を変更する、非常に一般的な操作です。それはおそらく、調査を追跡するためにいくつかの時間がかかるだろう、エラーの原因となるような詳細を忘れてしまいました。

文字列の操作

zend_stringAPIはまた、スケールアップなどまたは文字列ダウン他の操作を可能にする、または文字列比較文字列のケースを変更します。そこに参加操作のない使用はありませんが、また、実装するのは非常に簡単です:

zend_string *FOO, *bar, *foobar, *foo_lc;

FOO = zend_string_init("FOO", strlen("FOO"), 0);
bar = zend_string_init("bar", strlen("bar"), 0);

/* 和 C 字符串字面量比较 zend_string */
if (!zend_string_equals_literal(FOO, "foobar")) {
    foobar = zend_string_copy(FOO);

    /* realloc() 将 C 字符串分配到一个更大的 buffer 中 */
    foobar = zend_string_extend(foobar, strlen("foobar"), 0);

    /* 在重新分配了足够大的内存后连接 "bar" 和 "FOO"  */
    memcpy(ZSTR_VAL(foobar) + ZSTR_LEN(FOO), ZSTR_VAL(bar), ZSTR_LEN(bar));
}

php_printf("This is my new string: %s\n", ZSTR_VAL(foobar));

/* 比较两个 zend_string  */
if (!zend_string_equals(FOO, foobar)) {
    /* 复制一个字符串并将其转为小写 */
    foo_lc = zend_string_tolower(foo);
}

php_printf("This is FOO in lower-case: %s\n", ZSTR_VAL(foo_lc));

/* 释放内存 */
zend_string_release(FOO);
zend_string_release(bar);
zend_string_release(foobar);
zend_string_release(foo_lc);

zvalのアクセスzend_stringにより、

今、あなたが管理・運営する方法を知っていることをzend_string、私たちは彼らとのzvalコンテナの間の相互作用を見てみましょう。

  • あなたはおなじみのzvalをする必要があり、慣れていない場合は、お読みくださいZvals特別なセクションを。

あなたは、マクロがされます使用することができますzend_string保存されたzval中で、またはからzvalいくつかの読書zend_string

zval myval;
zend_string *hello, *world;

zend_string_init(hello, "hello", strlen("hello"), 0);

/* 将字符串存入 zval */
ZVAL_STR(&myval, hello);

/* 从 zval 中的 zend_string 读取 C 字符串 */
php_printf("The string is %s", Z_STRVAL(myval));

zend_string_init(world, "world", strlen("world"), 0);

/* 更改 myval 中的 zend_string:使用另一个 zend_string 替换它  */
Z_STR(myval) = world;

/* ... */

あなたはに、覚えているZSTR_***(s)マクロの始まりはに作用していますzend_string

  • ZSTR_VAL()
  • ZSTR_LEN()
  • ZSTR_HASH()
  • ...

すべてのZ_STR**(z)マクロ効果が埋め込まれ始めているzvalzend_string

  • Z_STRVAL()
  • Z_STRLEN()
  • Z_STRHASH()
  • ...

あなたがアクセスマクロを持っていない可能性があり、いくつかの他のがあります。

典型的な文字列の歴史の中でPHPとC

古典的なC文字列を簡単に紹介。C言語では、文字列は、文字列(あるchar foo[]()または文字ポインタchar *)。その長さは、彼らがNUL(あなたがその長さを知ることができ、文字列の先頭と末尾を知っている)の終わりになるでしょう理由である、不確実です。

PHP 7に先立ち、zend_string構造が存在しません。伝統の前にすることでchar * / int、ペアで使用すること。あなたはまた、まだ使用中のPHPのソースコードの一部を見つけることができますchar * / int代わりにzend_stringまた、APIのツールを見つけることができますzend_stringし、char * / int変換を。

可能な限り:使用しますzend_string希少な場所のいくつかは、使用していないzend_string場所の使用は問題ではありませんのでzend_string、しかし、PHPのソースコードを、あなたは多くの場所がされていますzend_string引用。

内部zend_string

ここでは簡単には文字列を抑留しました。拡張された開発ではめったにような概念を使用する必要がありません。インターンの文字列もしばしばOPCache拡張子と対話します。

インターンの文字列は、重複排除の文字列です。OPCacheで使用する場合、彼らはまた、別のリクエストに1つのリクエストから再利用します。

あなたは文字列「foo」を作成するとします。あなたは、単に新しい文字列「foo」を作成するだけです。

zend_string *foo;
foo = zend_string_init("foo", strlen("foo"), 0);

/* ... */

しかし、問題は次のとおりです。あなたがそれを使用する必要がある前に、その文字列がすでにまだ作成されていませんか?あなたは(つまり、「foo」というの一例である)あなたの前にいくつかのコードで必要とされる正確に同じ文字列を意味PHPの実行、のライフサイクルの中でいくつかの点で、あなたのコードの文字列を必要とするとき。

すでに保存されているインターンの文字列は、インターンの文字列のエンジンを探査し、(それはあなたの文字列を見つけることができるかどうか)がすでに割り当てられたポインタを再利用するように求められます。あなたがすることができない場合は、次の新しい文字列を作成する「インターン」の文字列として識別します。それはPHPのソース(など他の拡張、エンジン自体、)以外のシーンのために使用することができるように

次に例を示します。

zend_string *foo;
foo = zend_string_init("foo", strlen("foo"), 0);

foo = zend_new_interned_string(foo);

php_printf("This string is interned : %s", ZSTR_VAL(foo));

zend_string_release(foo);

上記のコードでは、我々は新しい、非常に古典を作成しましたzend_stringその後、我々は作成しますzend_stringへのパスをzend_new_interned_string()この関数は、エンジン内の文字列バッファセグメント(ここでは、「foo」という)で同じ文字列を検索します。あなたが(すでに作成したような文字列の前にという意味)それを見つけた場合、それは(おそらくそれを解放)あなたが作成した文字列を解放し、インターンバッファから文字の文字列で置き換えます。それが見つからない場合:それは使用後に他の場所でPHPまたはを使用するためにインターン文字列バッファに追加「foo」ということになります。

あなたはここでメモリ割り当てに注意を払わなければなりません。彼らはインターン文字列バッファと共有されますので、それらを破壊することはできませんので、もう一度数え参照する必要はないので、インターンの文字列は、常に、参照カウントを設定します。

例:

zend_string *foo, *foo2;

foo  = zend_string_init("foo", strlen("foo"), 0);
foo2 = zend_string_copy(foo); /* increments refcount of foo */

 /* refcount falls back to 1, even if the string is now
  * used at three different places */
foo = zend_new_interned_string(foo);

/* 当 foo 是 interned 字符串,这里什么也不会做 */
zend_string_release(foo);

/* 当 foo2 是 interned 字符串,这里什么也不会做 */
zend_string_release(foo2);

/* 进程结束后,PHP 将清楚 interned 字符串缓冲区,并且因此会对 "foo" 字符串进行 free()  */

次のガベージコレクションの部分です。

文字列が抑留されたときに、そのGCフラグが変更されるIS_STR_INTERNED、彼らがどのようなメモリ割り当てタイプ(永久又は要望に応じて)を使用するかどうかを識別する。あなたは、文字列をコピーしたり、解除したいときは、このフラグを検出します。文字列の型を抑留された場合、エンジンは、文字列の参照カウントを増加しません。あなたは、文字列を解放した場合でも、それは参照カウントが解放されることはありません減少しません。それは何もしません。プロセスのライフサイクルの終わりに、それは全体のインターン文字列バッファがあなたのインターンの文字列を解放します破壊します。

OPCacheがトリガされた場合、このプロセスはより複雑に実際にあります。拡大OPCacheを使用する方法を変更すると、文字列をインターン。あなたが要求インターンzend_stringを作成するプロセスである場合OPCacheがなければ、文字列は、現在のリクエストの終了時にクリアされ、次の要求で再利用されることはありません。あなたがOPCacheを使用する場合は、共有メモリブロックに格納された文字列を抑留し、このメモリ・ブロックを共有する各PHPプロセスの同じ「プール」の間で共有されます。さらに、要求はインターンストリングの複数の間で多重化されてもよいです。

、メモリを節約するためにインターンの文字列を使用しているため、文字列がメモリに保存されている同じ回数と一度を超えません。プロセスが多くのことを最適化されている場合でも、多くの場合、インターン文字列の店を見つけることが必要であるため、しかし、それは、いくつかのCPU時間を浪費することがあります。デザイナーの拡張として、次のようにいくつかのグローバル規則は、次のとおりです。

  • OPCacheを(使用する必要がある)場合は、バインディングリクエストを作成する必要がある場合は、文字列を読み取り専用:インターンの文字列は、それを使用しています。
  • あなたは(たとえば「PHP」または「str_replace」としても知られているPHPの文字列)インターン文字列を必要と知っていれば、それは、インターンの文字列を使用することです。
  • 文字列は、読み取り専用ではなく、/作成後に変更されなければならないことができれば、インターン文字列を使用しないでください。
  • 文字列は、将来的に再利用する可能性がない場合には、インターンの文字列を使用しないでください。

詳細については、参照文字列抑留されてもよいのZend / zend_string.cを

おすすめ

転載: www.cnblogs.com/ishenghuo/p/11122428.html
おすすめ