自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

變量改變時(shí)PHP內(nèi)核做了些什么?

開發(fā) 后端
內(nèi)容來自于《Extending and Embedding PHP》- Chaper 3 - Memory Management,加上自己的理解,對php中變量的引用計(jì)數(shù)、寫時(shí)復(fù)制,寫時(shí)改變,寫時(shí)復(fù)制和改變做個”翻譯“。

看下面的內(nèi)容之前先對zval這個結(jié)構(gòu)體做個了解

  1. typedef struct _zval_struct { 
  2.     zvalue_value value; 
  3.     zend_uint refcount; 
  4.     zend_uchar type; 
  5.     zend_uchar is_ref; 
  6. } zval; 

zval結(jié)構(gòu)體中共有4個元素,value是一個聯(lián)合體,用來真正的存儲zval的值,refcount用來計(jì)數(shù)該zval被多少個變量使用,type表示zval所存儲的數(shù)據(jù)類型,is_ref用來標(biāo)志該zval是否被引用。

引用計(jì)數(shù)

  1. <php 
  2.     $a = 'Hello World'
  3.     $b = $a; 
  4.     unset($a); 

我們一起來剖析下上面這段代碼:

  • $a = 'Hello World';首先這句代碼被執(zhí)行,內(nèi)核創(chuàng)建一個變量,并分配12字節(jié)的內(nèi)存去存儲字符串'Hello World'和末尾的NULL。

  • $b = $a;接著執(zhí)行這句代碼,執(zhí)行這句的時(shí)候內(nèi)核里面發(fā)生了什么呢?

    • $a所指向的zval中的refcount進(jìn)行加1操作。

    • 將變量$b指向$a所指向的zval。
      在內(nèi)核中大概是這樣的,其中active_symbol_table是當(dāng)前的變量符號表

      1.     zval *helloval; 
      2.     MAKE_STD_ZVAL(helloval); 
      3.     ZVAL_STRING(helloval, "Hello World"1); 
      4.     zend_hash_add(EG(active_symbol_table), "a", sizeof("a"), 
      5.                                         &helloval, sizeof(zval*), NULL); 
      6.     ZVAL_ADDREF(helloval); 
      7.     zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), 
      8.                                         &helloval, sizeof(zval*), NULL); 
  • unset($a);這句代碼執(zhí)行后,內(nèi)核會將a應(yīng)zval結(jié)構(gòu)refcount計(jì)數(shù)b還和原來一樣

寫時(shí)復(fù)制

 

  1. <?php 
  2.     $a = 1; 
  3.     $b = $a
  4.     $b += 5; 
  5. ?> 

上面這段代碼執(zhí)行完之后,一般肯定希望$a=1,$b=6,但是如果像引用計(jì)數(shù)那樣,$a$b指向相同的zval,修改$b之后$a不是也變了?
這個具體是怎么實(shí)現(xiàn)的呢,我們一起來看下:

  • $a = 1;內(nèi)核創(chuàng)建一個zval,并分配4個字節(jié)存儲數(shù)字1。

  • $b = $a;這一步和引用計(jì)數(shù)中的第二步一樣,將$b指向和$a相同的zval,并將zval中的引用計(jì)數(shù)值refcount加1。

  • $b += 5;關(guān)鍵是這一步,這一步驟發(fā)生了什么呢,怎么確保修改之后不影響$a

    • 其實(shí)Zend內(nèi)核在改變zval之前都會去進(jìn)行get_var_and_separete操作,如果recfount>1,就需要分離就創(chuàng)建新的zval返回,否則直接返回變量所指向的zval,下面看看如何分離產(chǎn)生新的zval。

    • 復(fù)制一個和$b所指向zval一樣的zval。

    • $b所指向的zval中的refcount計(jì)數(shù)減1。

    • 初始化生成的新zval,設(shè)置refcount=1,is_ref=0。

    • $b指向新生成的zval。

    • 對新生成的zval進(jìn)行操作,這就是寫時(shí)復(fù)制。
      下面看看內(nèi)核中分離時(shí)的主要代碼:

      1. zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC) 
      2.     zval **varval, *varcopy; 
      3.     if (zend_hash_find(EG(active_symbol_table), 
      4.                     varname, varname_len + 1, (void**)&varval) == FAILURE) { 
      5.     /* Variable doesn't actually exist  fail out */ 
      6.     return NULL; 
      7. if ((*varval)->is_ref || (*varval)->refcount < 2) { 
      8.     /* varname is the only actual reference, 
      9.     * or it's a full reference to other variables 
      10.     * either way: no separating to be done 
      11.     */ 
      12.     return *varval; 
      13. /* Otherwise, make a copy of the zval* value */ 
      14. MAKE_STD_ZVAL(varcopy); 
      15. varcopy = *varval; 
      16. /* Duplicate any allocated structures within the zval* */ 
      17. zval_copy_ctor(varcopy); 
      18.  
      19. /* Remove the old version of varname 
      20. * This will decrease the refcount of varval in the process 
      21. */ 
      22. zend_hash_del(EG(active_symbol_table), varname, varname_len + 1); 
      23.  
      24. /* Initialize the reference count of the 
      25. * newly created value and attach it to 
      26. * the varname variable 
      27. */ 
      28. varcopy->refcount = 1; 
      29. varcopy->is_ref = 0; 
      30. zend_hash_add(EG(active_symbol_table), varname, varname_len + 1, 
      31.                                         &varcopy, sizeof(zval*), NULL); 
      32. /* Return the new zval* */ 
      33. return varcopy; 

寫時(shí)改變

 

  1. <?php 
  2.     $a = 1; 
  3.     $b = &$a
  4.     $b += 5; 
  5. ?> 

上面這段代碼執(zhí)行完之后一般希望是:$a == $b == 1。這個又是怎么實(shí)現(xiàn)的呢?

  • $a = 1;這一步驟和寫時(shí)復(fù)制中的***步一樣。

  • $b = &$a;這一步驟內(nèi)核會將$b指向$a所指向的zval,將zval中的refcount加1,并將zval中的is_ref置為1。

  • $b += 5;這一步驟和寫時(shí)復(fù)制中的第三步驟一樣,但是內(nèi)核中發(fā)生的事情卻不一樣。

    • 內(nèi)核看到$b進(jìn)行變化的時(shí)候,也會執(zhí)行get_var_and_separate函數(shù),看是否需要分離。

    • 如果(*varval)->is_ref的話也會直接返回$b所指向的zval,不去分離產(chǎn)生新的zval,不管zval的refcount是否>1。

    • 這時(shí)候再去修改$b值,$a的值也就改變了,因?yàn)樗麄冎赶蛳嗤膠val。

分離的問題

說道現(xiàn)在聰明的你可能已經(jīng)看出點(diǎn)問題了,如果一個zval結(jié)構(gòu)體既有refcount計(jì)數(shù)又有is_ref引用這個時(shí)候怎么辦?

  1. <?php 
  2.     $a = 1; 
  3.     $b = $a
  4.     $c = &$a
  5. ?> 

如果出現(xiàn)上面這種情況的時(shí)候,如果$a、$b、$c指向同一個zval結(jié)構(gòu)體,進(jìn)行改變的時(shí)候Zend到底去聽誰的?其實(shí)這個地方不會指向同一個zval了。
如果對一個is_ref = 0 && refcount >1的zval進(jìn)行寫時(shí)改變這種賦值形式(就是引用賦值)的時(shí)候,Zend會將等號右邊的變量分離出來一個新的zval,
對這個zval進(jìn)行初始化,對之前的zval的refcount進(jìn)行減1操作,讓等號左邊的變量指向這個新的zval,refcount進(jìn)行加1操作,is_ref=1??纯聪旅孢@張圖片


  1. <?php 
  2.     $a = 1; 
  3.     $b = &$a
  4.     $c = $a
  5. ?> 

上面這又是另外一種情況,在is_ref = 1的情況下,試圖單純的進(jìn)行refcount+1操作的時(shí)候會分離出來一個新的zval給等號左邊的變量,并初始化他,看看下面這張圖片

參考文獻(xiàn)

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

責(zé)任編輯:王雪燕 來源: 博客園
相關(guān)推薦

2011-07-01 08:41:12

PHP

2021-01-03 10:37:50

前端開發(fā)技術(shù)

2017-10-21 10:12:05

戴爾

2011-07-07 14:28:23

PHP

2009-12-09 13:03:12

2019-05-14 09:18:18

程序員PythonJava

2017-11-13 15:48:36

架構(gòu)Spring Clou演進(jìn)

2017-11-14 09:03:36

Spring Clou架構(gòu)演進(jìn)

2019-12-10 11:01:06

云計(jì)算/預(yù)判性科研/自

2017-07-19 16:58:53

PHPFastCGI 內(nèi)核探索

2019-12-09 09:50:18

程序員技能開發(fā)者

2024-03-28 14:16:43

容災(zāi)云計(jì)算

2017-07-27 14:21:40

phpPHP源碼分析hashtable

2009-12-04 13:31:21

PHP全局變量不能生效

2019-05-16 09:07:42

華為方舟編譯器

2021-03-15 08:40:42

Vue組件函數(shù)

2016-08-12 10:11:22

2016-12-21 10:35:55

PHP內(nèi)核PHP哈希表

2011-07-06 10:37:46

開發(fā)技術(shù)周刊

2014-03-31 10:47:49

PHPASP.NET
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號