目次
変数が変更されると、PHP カーネルは何をしますか?
引用
ズヴァル
参照数
写时复制
写时改变
分離の問題
参考文献
ホームページ バックエンド開発 PHPチュートリアル 変数が変更されると、PHP カーネルは何をしますか? _PHP チュートリアル

変数が変更されると、PHP カーネルは何をしますか? _PHP チュートリアル

Jul 12, 2016 am 09:08 AM
PHPカーネル PHP変数

変数が変更されると、PHP カーネルは何をしますか?

引用

内容は、「PHP の拡張と埋め込み」 - 第 3 章 - メモリ管理に私自身の理解を加えたもので、参照カウント、コピーオンライト、チェンジオンライト、コピーオンライト、および変更を「変換」します。 PHP の変数の説明。

ズヴァル

以下の内容を読む前に、まず zval 構造を理解してください

リーリー

zval 構造体には 4 つの要素があり、zval の値を実際に格納するために使用される共用体です。refcount は、zval に格納されているデータ型を表す変数の数をカウントするために使用されます。 zval が参照されているかどうか。

参照数

リーリー

上記のコードを一緒に分析してみましょう:

  • $a = 'Hello World';最初にこのコードが実行され、カーネルは変数を作成し、文字列「Hello World」と最後に NULL を格納するために 12 バイトのメモリを割り当てます。
  • $b = $a;次に、このコードを実行します。この文を実行すると、カーネルで何が起こるでしょうか?

    • が指す zval の refcount に 1 を追加します。 $a
    • 変数 $b は、

      が指す zval を指します。 $b指向$a カーネル内では次のようになります。ここで、
      は現在の変数シンボルテーブルですactive_symbol_table

      <code class="hljs clojure" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;">    <span class="hljs-collection">{
              zval *helloval;
              MAKE_STD_ZVAL<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">helloval</span>)</span><span class="hljs-comment" style="color: green;">;</span>
              ZVAL_STRING<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">helloval</span>, <span class="hljs-string" style="color: rgb(163, 21, 21);">"Hello World"</span>, <span class="hljs-number">1</span>)</span><span class="hljs-comment" style="color: green;">;</span>
              zend_hash_add<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">EG</span><span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">active_symbol_table</span>)</span>, <span class="hljs-string" style="color: rgb(163, 21, 21);">"a"</span>, sizeof<span class="hljs-list">(<span class="hljs-string" style="color: rgb(163, 21, 21);">"a"</span>)</span>,
                                                  &helloval, sizeof<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">zval*</span>)</span>, NULL)</span><span class="hljs-comment" style="color: green;">;</span>
              ZVAL_ADDREF<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">helloval</span>)</span><span class="hljs-comment" style="color: green;">;</span>
              zend_hash_add<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">EG</span><span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">active_symbol_table</span>)</span>, <span class="hljs-string" style="color: rgb(163, 21, 21);">"b"</span>, sizeof<span class="hljs-list">(<span class="hljs-string" style="color: rgb(163, 21, 21);">"b"</span>)</span>,
                                                  &helloval, sizeof<span class="hljs-list">(<span class="hljs-keyword" style="color: rgb(0, 0, 255);">zval*</span>)</span>, NULL)</span><span class="hljs-comment" style="color: green;">;</span>
          }</span></code>
      ログイン後にコピー
  • unset($a);このコードが実行されると、カーネルは a に応答します z val結び目構造 refcountメーターカウント マイナス b还和原来一样

写时复制

<code class="hljs xml" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;"><span class="php"><span class="hljs-preprocessor" style="color: rgb(43, 145, 175);"><?php</span>
    <span class="hljs-variable">$a</span> = <span class="hljs-number">1</span>;
    <span class="hljs-variable">$b</span> = <span class="hljs-variable">$a</span>;
    <span class="hljs-variable">$b</span> += <span class="hljs-number">5</span>;
<span class="hljs-preprocessor" style="color: rgb(43, 145, 175);">?></span></span></code>
ログイン後にコピー

上面这段代码执行完之后,一般肯定希望$a=1,$b=6,但是如果像引用计数那样,$a$b指向相同的zval,修改$b之后$a不是也变了?
这个具体是怎么实现的呢,我们一起来看下:

  • $a = 1;カーネルは zval を作成し、数値 1 を格納するために 4 バイトを割り当てます。
  • $b = $a;这一步和引用计数中的第二步一样,将$b指向和$a同じzvalで、zval内の参照カウント値refcountに1を加算します。
  • $b += 5;关键是这一步,这一步骤发生了什么呢,怎么确保修改之后不影响$a

    • 其实Zend内核在改变zval之前都会去进行get_var_and_separete操作,如果recfount>1,就需要分离就创建新的zval返回,否则直接返回变量所指向的zval,下面看看如何分离产生新的zval。
    • 复制一个和$b所指向zval一样的zval。
    • $b所指向的zval中的refcount计数减1。
    • 初始化生成的新zval,设置refcount=1,is_ref=0。
    • $b指向新生成的zval。
    • 对新生成的zval进行操作,这就是写时复制。
      下面看看内核中分离时的主要代码:

      <code class="hljs lasso" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;">    zval <span class="hljs-subst">*</span>get_var_and_separate(char <span class="hljs-subst">*</span>varname, int varname_len TSRMLS_DC)
          {
              zval <span class="hljs-subst">**</span>varval, <span class="hljs-subst">*</span>varcopy;
              <span class="hljs-keyword" style="color: rgb(0, 0, 255);">if</span> (zend_hash_find(EG(active_symbol_table),
                              varname, varname_len <span class="hljs-subst">+</span> <span class="hljs-number">1</span>, (<span class="hljs-literal">void</span><span class="hljs-subst">**</span>)<span class="hljs-subst">&</span>varval) <span class="hljs-subst">==</span> FAILURE) {
              <span class="hljs-comment" style="color: green;">/* Variable doesn't actually exist  fail out */</span>
              <span class="hljs-keyword" style="color: rgb(0, 0, 255);">return</span> <span class="hljs-built_in" style="color: rgb(0, 0, 255);">NULL</span>;
          }
          <span class="hljs-keyword" style="color: rgb(0, 0, 255);">if</span> ((<span class="hljs-subst">*</span>varval)<span class="hljs-subst">-></span>is_ref <span class="hljs-subst">||</span> (<span class="hljs-subst">*</span>varval)<span class="hljs-subst">-></span>refcount <span class="hljs-subst"><</span> <span class="hljs-number">2</span>) {
              <span class="hljs-comment" style="color: green;">/* varname is the only actual reference,
              * or it&#39;s a full reference to other variables
              * either way: no separating to be done
              */</span>
              <span class="hljs-keyword" style="color: rgb(0, 0, 255);">return</span> <span class="hljs-subst">*</span>varval;
          }
          <span class="hljs-comment" style="color: green;">/* Otherwise, make a copy of the zval* value */</span>
          MAKE_STD_ZVAL(varcopy);
          varcopy <span class="hljs-subst">=</span> <span class="hljs-subst">*</span>varval;
          <span class="hljs-comment" style="color: green;">/* Duplicate any allocated structures within the zval* */</span>
          zval_copy_ctor(varcopy);
      
          <span class="hljs-comment" style="color: green;">/* Remove the old version of varname
          * This will decrease the refcount of varval in the process
          */</span>
          zend_hash_del(EG(active_symbol_table), varname, varname_len <span class="hljs-subst">+</span> <span class="hljs-number">1</span>);
      
          <span class="hljs-comment" style="color: green;">/* Initialize the reference count of the
          * newly created value and attach it to
          * the varname variable
          */</span>
          varcopy<span class="hljs-subst">-></span>refcount <span class="hljs-subst">=</span> <span class="hljs-number">1</span>;
          varcopy<span class="hljs-subst">-></span>is_ref <span class="hljs-subst">=</span> <span class="hljs-number">0</span>;
          zend_hash_add(EG(active_symbol_table), varname, varname_len <span class="hljs-subst">+</span> <span class="hljs-number">1</span>,
                                                  <span class="hljs-subst">&</span>varcopy, sizeof(zval<span class="hljs-subst">*</span>), <span class="hljs-built_in" style="color: rgb(0, 0, 255);">NULL</span>);
          <span class="hljs-comment" style="color: green;">/* Return the new zval* */</span>
          <span class="hljs-keyword" style="color: rgb(0, 0, 255);">return</span> varcopy;
          }</code>
      ログイン後にコピー

写时改变

<code class="hljs xml" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;"><span class="php"><span class="hljs-preprocessor" style="color: rgb(43, 145, 175);"><?php</span>
    <span class="hljs-variable">$a</span> = <span class="hljs-number">1</span>;
    <span class="hljs-variable">$b</span> = &<span class="hljs-variable">$a</span>;
    <span class="hljs-variable">$b</span> += <span class="hljs-number">5</span>;
<span class="hljs-preprocessor" style="color: rgb(43, 145, 175);">?></span></span></code>
ログイン後にコピー

上面这段代码执行完之后一般希望是:$a == $b == 6。这个又是怎么实现的呢?

  • $a = 1;このステップは、コピーオンライトの最初のステップと同じです。
  • $b = &$a;这一步骤内核会将$b指向$a が指す zval については、zval の refcount に 1 を加算し、zval の is_ref を 1 に設定します。
  • $b += 5;このステップはコピーオンライトの 3 番目のステップと同じですが、カーネル内で何が起こるかが異なります。

    • カーネルは $b の変更を認識すると、get_var_and_ Separate 関数も実行して、分離が必要かどうかを確認します。
    • If (*varval)->is_ref も、個別に生成せずに (*varval)->is_ref的话也会直接返回$b が指す zval を直接返します。 zval の refcount が 1 より大きいかどうかには関係ありません。
    • この時点で $b值,$a の値を変更します、$a は同じ zval を指しているため、値も変わります。

分離の問題

賢明な方は、何か間違ったことに気づいたかもしれません。zval 構造体に refcount count と is_ref 参照の両方がある場合はどうなるでしょうか?

<code class="hljs xml" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;"><span class="php"><span class="hljs-preprocessor" style="color: rgb(43, 145, 175);"><?php</span>
    <span class="hljs-variable">$a</span> = <span class="hljs-number">1</span>;
    <span class="hljs-variable">$b</span> = <span class="hljs-variable">$a</span>;
    <span class="hljs-variable">$c</span> = &<span class="hljs-variable">$a</span>;
<span class="hljs-preprocessor" style="color: rgb(43, 145, 175);">?></span></span></code>
ログイン後にコピー

如果出现上面这种情况的时候,如果$a、$b、$c指向同一个zval结构体,进行改变的时候Zend到底去听谁的?其实这个地方不会指向同一个zval了。
如果对一个is_ref = 0 && refcount >1的zval进行写时改变这种赋值形式(就是引用赋值)的时候,Zend会将等号右边的变量分离出来一个新的zval,
对这个zval进行初始化,对之前的zval的refcount进行减1操作,让等号左边的变量指向这个新的zval,refcount进行加1操作,is_ref=1。看看下面这张图片

変数が変更されると、PHP カーネルは何をしますか? _PHP チュートリアル

<code class="hljs xml" style="font-family: 'Courier New', sans-serif !important; line-height: 1.5 !important; font-size: 12px !important; background-color: rgb(245, 245, 245) !important; border: 1px solid rgb(204, 204, 204) !important; padding: 5px !important; border-top-left-radius: 3px !important; border-top-right-radius: 3px !important; border-bottom-right-radius: 3px !important; border-bottom-left-radius: 3px !important; display: block; overflow-x: auto; color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial;"><span class="php"><span class="hljs-preprocessor" style="color: rgb(43, 145, 175);"><?php</span>
    <span class="hljs-variable">$a</span> = <span class="hljs-number">1</span>;
    <span class="hljs-variable">$b</span> = &<span class="hljs-variable">$a</span>;
    <span class="hljs-variable">$c</span> = <span class="hljs-variable">$a</span>;
<span class="hljs-preprocessor" style="color: rgb(43, 145, 175);">?></span></span></code>
ログイン後にコピー

上面这又是另外一种情况,在is_ref = 1的情况下,试图单纯的进行refcount+1操作的时候会分离出来一个新的zval给等号左边的变量,并初始化他,看看下面这张图片

変数が変更されると、PHP カーネルは何をしますか? _PHP チュートリアル

参考文献

1.《Extending and Embedding PHP》- Chaper 3 - Memory Management.


www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1056558.htmlTechArticle变量改变时PHP内核做了些什么? 引言 内容来自于《Extending and Embedding PHP》- Chaper 3 - Memory Management,加上自己的理解,对php中变量的引用计...
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP 注意: 未定義の変数: 解決策 PHP 注意: 未定義の変数: 解決策 Jun 25, 2023 pm 04:18 PM

PHP 開発では、PHP Notice:Unknownvariable というエラー メッセージが頻繁に発生します。このエラー メッセージは、コード内で未定義の変数が使用されたことを意味します。このエラー メッセージによってコードがクラッシュすることはありませんが、コードの可読性と保守性に影響します。以下、この記事ではこのエラーを解決するいくつかの方法を紹介します。 1. 開発プロセス中に error_reporting(E_ALL) 関数を使用します。PHP 開発では、次のことができます。

PHP 注意: 未定義の変数: ソリューション内の arr PHP 注意: 未定義の変数: ソリューション内の arr Jun 22, 2023 am 10:21 AM

PHP Notice:Unknownvariable:arrin の解決策 PHP プログラミングでは、「 Notice:Unknownvariable」というエラー メッセージが頻繁に表示されます。このエラー メッセージは通常、未定義の変数にアクセスしたか、変数が初期化されていないことが原因で発生します。この問題については、問題を見つけて時間内に解決する必要があります。この記事では、PHP Notice:Undefin に焦点を当てます。

PHP で数値変数を使用する方法 PHP で数値変数を使用する方法 Sep 13, 2023 pm 12:46 PM

PHP で数値変数を使用する方法 PHP では、数値変数は、宣言せずに直接使用される変数の型です。数値変数を使用して、数学的計算、データ比較、その他の数値演算を実行できます。この記事では、PHP で数値変数を使用する方法を説明し、具体的なコード例を示します。数値変数の定義 PHP では、数値変数の定義は非常に簡単で、変数に直接数値を割り当てるだけです。以下に例を示します: $number=10; 上記のコードでは、$numb という値を定義します。

PHP 変数の未定義エラーをすばやく排除するにはどうすればよいですか? PHP 変数の未定義エラーをすばやく排除するにはどうすればよいですか? Dec 17, 2023 am 10:23 AM

PHP 変数の未定義エラーをすばやく排除するにはどうすればよいですか? PHP 開発では、未定義変数エラーがよく発生します。これは、コード内で未割り当ての変数が使用されているためです。この種のエラーが発生した場合、エラーの原因を迅速に特定して解決する必要があります。ここでは、PHP 変数の未定義エラーを迅速にトラブルシューティングして、エラーをより迅速に見つけて修正するためのいくつかの方法を示します。エラーレポートをオンにする: エラーレポートをオンにすると、PHP は変数未定義エラーを含むすべてのエラーおよび警告メッセージを表示します。コードを開くことでこれを行うことができます

PHP 注意: 未定義の変数: SQL ソリューション PHP 注意: 未定義の変数: SQL ソリューション Jun 23, 2023 am 08:51 AM

PHP アプリケーションを開発するときに、「未定義の変数: sql」というプロンプトが表示された場合は、通常、未定義の変数を参照していることを意味します。これには、変数名のスペルミス、スコープの問題、コード内の構文エラーなど、さまざまな理由が考えられます。この記事では、この問題のさまざまな原因を調査し、それを解決するいくつかの方法を提供します。 1. 変数名のスペルが間違っている PHP コード内で変数名が間違っているか、スペルが間違っている場合、システムは

PHP に関する解決策 注意: 未定義の変数: 結果 PHP に関する解決策 注意: 未定義の変数: 結果 Jun 22, 2023 pm 01:32 PM

PHP Notice:Unknownvariable:result は、PHP プログラムで未定義変数の結果が呼び出され、プログラムが通知レベルの警告を生成することを意味します。この状況は通常、プログラマが PHP コードを作成するときに変数または変数のスコープを正しく定義していないことが原因で発生します。この通知レベルの警告が時間内に解決されない場合、プログラムの動作に問題が発生する可能性があります。したがって、PHP Notice を解決する方法は次のとおりです。

PHP変数を参照によって渡す方法 PHP変数を参照によって渡す方法 Aug 26, 2023 am 09:01 AM

PHP では、アンパサンド (&) 記号を使用して、値ではなく参照によって変数を渡すことができます。これにより、関数またはメソッド内で元の変数を変更できるようになります。 PHP 変数を参照渡しするには、主に 2 つの方法があります。 アンパサンド記号を使用する 関数/メソッド宣言でアンパサンド記号を使用する 関数/メソッド宣言でアンパサンド記号を使用する 関数/メソッドに変数を渡す場合、PHP では関数/ アンパサンド記号 ( &) メソッド宣言で変数を参照渡しします。更新された説明は次のとおりです。関数/メソッド宣言で & 記号を使用して参照変数を渡すには、関数/メソッド定義のパラメーター名の前に & 記号を含める必要があります。これは、パラメーターが参照によって渡される必要があることを示します。

PHP7 の基礎となる開発原則への入門ガイド: PHP カーネルの秘密をゼロから学ぶ PHP7 の基礎となる開発原則への入門ガイド: PHP カーネルの秘密をゼロから学ぶ Sep 08, 2023 pm 04:34 PM

PHP7 の基礎となる開発原理の入門ガイド: PHP カーネルの秘密をゼロから学ぶ はじめに: インターネットの急速な発展に伴い、人気のあるサーバーサイド スクリプト言語としての PHP には、幅広いアプリケーション シナリオがあります。しかし、多くの人は PHP の内部と仕組みについてほとんど知りません。この記事は、PHP カーネルを深く理解したい開発者向けに、PHP カーネルの謎をゼロから学ぶのに役立つ入門ガイドを提供します。 1. PHP カーネルの基本概念 PHP のコンパイル処理 PHP のコンパイル処理について

See all articles