成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

變量改變時PHP內核做了些什么?

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

看下面的內容之前先對zval這個結構體做個了解

  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結構體中共有4個元素,value是一個聯合體,用來真正的存儲zval的值,refcount用來計數該zval被多少個變量使用,type表示zval所存儲的數據類型,is_ref用來標志該zval是否被引用。

引用計數

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

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

  • $a = 'Hello World';首先這句代碼被執行,內核創建一個變量,并分配12字節的內存去存儲字符串'Hello World'和末尾的NULL。

  • $b = $a;接著執行這句代碼,執行這句的時候內核里面發生了什么呢?

    • $a所指向的zval中的refcount進行加1操作。

    • 將變量$b指向$a所指向的zval。
      在內核中大概是這樣的,其中active_symbol_table是當前的變量符號表

      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);這句代碼執行后,內核會將azvalrefcountb還和原來一樣

寫時復制

 

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

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

  • $a = 1;內核創建一個zval,并分配4個字節存儲數字1。

  • $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進行操作,這就是寫時復制。
      下面看看內核中分離時的主要代碼:

      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; 

寫時改變

 

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

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

  • $a = 1;這一步驟和寫時復制中的***步一樣。

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

  • $b += 5;這一步驟和寫時復制中的第三步驟一樣,但是內核中發生的事情卻不一樣。

    • 內核看到$b進行變化的時候,也會執行get_var_and_separate函數,看是否需要分離。

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

    • 這時候再去修改$b值,$a的值也就改變了,因為他們指向相同的zval。

分離的問題

說道現在聰明的你可能已經看出點問題了,如果一個zval結構體既有refcount計數又有is_ref引用這個時候怎么辦?

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

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


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

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

參考文獻

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

責任編輯:王雪燕 來源: 博客園
相關推薦

2011-07-01 08:41:12

PHP

2021-01-03 10:37:50

前端開發技術

2017-10-21 10:12:05

戴爾

2011-07-07 14:28:23

PHP

2019-05-14 09:18:18

程序員PythonJava

2009-12-09 13:03:12

2017-11-14 09:03:36

Spring Clou架構演進

2017-11-13 15:48:36

架構Spring Clou演進

2017-07-19 16:58:53

PHPFastCGI 內核探索

2019-12-10 11:01:06

云計算/預判性科研/自

2019-12-09 09:50:18

程序員技能開發者

2024-03-28 14:16:43

容災云計算

2017-07-27 14:21:40

phpPHP源碼分析hashtable

2021-03-15 08:40:42

Vue組件函數

2019-05-16 09:07:42

華為方舟編譯器

2009-12-04 13:31:21

PHP全局變量不能生效

2016-08-12 10:11:22

2016-12-21 10:35:55

PHP內核PHP哈希表

2011-04-19 10:04:25

NeopPIshell網站后門

2025-02-21 10:01:35

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国内毛片毛片毛片毛片 | 国产久 | 久久精品国产亚洲一区二区三区 | 涩涩视频在线观看 | 99re国产视频 | 久久精品一二三影院 | 国产成人自拍一区 | 天天干在线播放 | 农村真人裸体丰满少妇毛片 | 特黄毛片视频 | 草久视频| 91在线一区 | 久久国产精品网站 | 天天干天天想 | 日韩精品久久 | 天天爱天天操 | 91免费版在线观看 | 久久综合一区 | 黑人精品欧美一区二区蜜桃 | 精品国产欧美一区二区 | 久色网| 欧美极品少妇xxxxⅹ免费视频 | 久久精品一区 | 亚洲欧美精品 | 国产97久久 | 成人日批视频 | 五月婷婷丁香 | 日韩中文字幕免费在线 | 资源首页二三区 | 久久免费观看一级毛片 | 免费毛片网站在线观看 | 欧美日韩手机在线观看 | 成人国产精品免费观看 | 国产一区二区视频在线观看 | 国产精品视频网站 | 国产亚洲黄色片 | 国产在线精品一区二区 | 色综合色综合网色综合 | 自拍偷拍第一页 | www.国产| 在线中文字幕第一页 |