摘要:本文主要是針對,的話可以移步到慶哥的博客看,還有就是小菜我讀的是內核剖析這本書。接下來我會使用到來調試源碼本文有參照博客中的部分內容以及代碼。
前言
工作+實習快一年了,搞php后端開發,一直很迷茫怎么提高自己,就先從php源碼開始吧,本人比較菜,本文章寫的比較趕時間,所以有什么錯誤或者漏掉的地方,望各位大神指正,多交流才能成長嘛,嘿嘿。
本文主要是針對php7,php5的話可以移步到慶哥的博客看,還有就是小菜我讀的是《php7內核剖析》這本書。
接下來我會使用到xdebug來調試php源碼
本文有參照ohmygirl博客中的部分內容以及代碼。
本文所用環境為windows,php7.0.10
php7中zval,zend_value的基本結構php7和php5不同的地方有很多,zval,zend_value結構就是其中之一
在php7中
zval定義在zend_types.h中
在zval這個結構體重包含三個部分 zend_value(存儲實際的內容),u1,u2兩個聯合體,其中u1主要存儲變量相關的一下屬性,而u2則是對u1的一些補充,例如當用到數組的時候,會用到u2.next來解決key哈希后出現的hash沖突
struct _zval_struct { zend_value value; /* 存儲變量的實際內容 */ union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* 存儲變量的類型 */ zend_uchar type_flags, /* 用于標識變量狀態,例如GC方面的管理,通過設置為IS_TYPE_COLLECTABLE 則變量會被收集到GC中回收垃圾的buffer緩存區中 */ zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ uint32_t extra; /* not further specified */ } u2; };
zend_uchar type: 以下為外部使用的變量類型
#define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10
php7中zend_value結構
typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; /*用于統計計數用,*/ zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
這里我們先解釋一下php7的zval,zend_valu中重要的的幾個變量
zval中:
(1)zend_uchar type 這個是用來表示當前變量(例如 $a)是什么類型的變量($a是string類型?還是int類型?還是引用類型....)
zend_value中:
(1)zend_refcounted *counted; 表示引用計數的次數, 何為引用計數? 就是zend_value變量被zval引用的次數,例如我們$a=“abcs”;$c=$a;$b=$a;此時counted=3,如下圖,其中refcount也是實現GC自動內存回收的基礎,下面會詳細講解php7變量的內部實現
php7中對與變量的實現分以下幾種方式
(1).對于boolen類型,還有null,undefined,這種沒有具體值,只有類型的類型,直接在zval中通過zend_uchar type的類型來判斷,無需通過引用計數來實現。
正是因為沒有通過應用計數來實現,所以它refcount為0
(2)對于int類型和float類型,因為在zend_value中有zend_long和double來保存數據,如下圖,所以,在賦值的時候就不需要再使用引用計數了,在拷貝的直接進行賦值就行了,這樣做可以省掉大量引用計數的相關操作
定義一個$a=1,在php內核中zval和zend_value的關系
(3)第三種就是常規的使用引用計數的方式來進行來進行變量的定義。在這些變量的實現中,都是通過指針指向一個具體的數據類型
例如 :
zend_string *str; struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1]; };
$假設我定義一個$a="111";其內部的實現就是,注意這里zend_string中char val[1],主要是因為C語言中字符串是以“0”結尾的,所以在zend_string中$a="111"這個變量值的存儲是char val[4]="111"。
1.普通賦值
前面說到,在php中,定義一個變量$a="444",實際上是生成了一個zval,和一個zend_value,然后zval指向這個zend_value來實現對$a="444"的定義的,然后通過refcount來統計引用的次數。
**所以可以總結出,refcount表示當前有多少個zval指向同一個zend_value**
我們定義如下:
$a="111";
$b=$a;
當然對于像boole型還有int,double,null變量,他們的直接通過zval保存,不會公用一個zend_value,所以直接使用深拷貝。
至于什么是深拷貝,什么是淺拷貝,最直接的區別就是在于有沒有重新生成一個一模一樣的zend_value,詳細請參看深拷貝和淺拷貝。
后面的寫時賦值(COW copy on write)就會使用到深拷貝。
對于php的賦值,實際上并不是所有的類型都是一樣的,剛剛也有說到,在php的zval中就有一個專門的字段用于標識當前類型適合哪種形式的那就是。
zend_uchar type_flags,
| refcounted | collectable | copyable | immutable ----------------+------------+-------------+----------+---------- simple types | | | | string | x | | x | interned string | | | | array | x | x | x | immutable array | | | | x object | x | x | | resource | x | | | reference | x | | |
zend_uchar type_flags這個字段用于標識當前的zval的屬于哪種賦值方式或者處于哪種狀態,主要是用于內存方面的管理具體參見內存管理,
2.引用賦值
php中的引用賦值就是我們常用的引用,例如$a=&$b,這樣。
在php7中實現引用的時候,在php中實現引用的時候,會先生成一個zend_reference類型,這個類型中嵌套一個zval,然后這個zval的zend_value會指向之前的之前的那個zval的zend_value,原來的那個zval類型轉換成IS_REFERENCE類型,簡單來說**就是將原有的zval類型轉換成IS_REFERENCE,并新生成zend_reference類型指向原zend的zend_value。(好jb繞,理了好久)。
struct _zend_reference { zend_refcounted_h gc; zval val; };
例如我們定義一個$a="1111";$b=&$a;它的變換如下圖
$a="111"
$b=&$a;
可能有朋友注意到了,在zend_reference中refcount為2 ,但是在最后的真實的zend_value為refcount為1,這是為什么呢,其實很好理解,前面說過,refcount表示有多少個zval指向該zend_value,zend_reference中只有一個zval指向了zend_value。
中間加一層zend_reference這樣做其實有很多好處,這樣可以保留原有的zend_value類型不變,為深拷貝操作提供拷貝條件,下面我們舉個例子就知道了
是不是一目了然,只能說開發內核的那些神牛太牛逼了。。。。。。。
當然關于php中值傳遞不僅僅那么簡單,還有很多很復雜的東西,小菜我也是才看沒多久,望各位大牛勿噴
php中的COW (copy on write)寫時復制是一種很重要的優化手段,這里涉及到深拷貝和淺拷貝的知識,請移步。
關于深拷貝,剛剛上面的這個例子就是很好的說明
所謂深拷貝就是將原有的數據拷貝一份放到獨立分配一個地址空間。
而寫時賦值就是對深拷貝的一種優化吧,意思是只有當發生寫操作的時候才進行深拷貝
舉個例子:
$a="3333"; $b=$a; $b.="444";
$a="3333";
$b=$a;
這個操作會使兩個zval指向同一個zend_value
這里并沒有觸發COW,執行深拷貝
當$b.="444";發生了寫操作的時候,觸發COW,執行深拷貝,拷貝了完全一樣的一份zend_value,$b所在的zval由原來的和$a所在的zval共同指向之前的zend_value, 轉換成指向拷貝出的新的zend_value
如果不進行深拷貝的話,那么當執行$b.="444";后,$a也會等于“3333444”
當然不是所有的zend_value類型都可以進行復制,這個請參見我之前的那個zend_uchar type_flags表
**文章中可能有些不足的地方,懇求指正。
本來打算再寫寫GC回收的原理的,但是現在已經2點多了,23333333.明天還要繼續打碼呢。。。。。。。不好意思**
下一篇準備寫一下php中的數組的實現;
lift needs art,i need girl
http://bbs.csdn.net/topics/39...
http://blog.csdn.net/black_ox...
http://blog.csdn.net/xiaolei1...
https://segmentfault.com/a/11...
https://github.com/laruence/p...
https://www.cnblogs.com/ohmyg...
可能有遺漏的參考博客,望博主見諒
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28175.html
摘要:垃圾回收所謂垃圾就是指通過循環引用自己引用自己,目前只在類型中有出現的形式而導致永遠不為。當出現垃圾之后,的引擎有對應的垃圾回收機制。觸發這個機制的時機是每次出現減少時候。 自嘲)。。。。。2333,我覺得這是因為在php語言層面就幫我們解決了內存回收的問題,但這讓我在和java大牛們吹牛逼的時候,聽到什么內存泄露。。。。(納尼,我tmd怎么從來沒遇見過)一臉懵逼。 本人小菜,如果下面...
摘要:插入一個元素時先將元素按先后順序插入數組,位置是,再根據的哈希值映射到散列表中的某個位置,將存入這個位置查找時先在散列表中映射到,得到在數組的位置,再從數組中取出元素。目前只有兩種類型會使用這種機制。 1.變量結構 typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:告訴引擎要取的參數的信息,用來確保線程安全,返回值檢測是還是。數組遍歷假設我們需要一個取代以下功能的擴展的遍歷數組和差很多,提供了一些專門的宏來遍歷元素或。是一個關于線程安全的動作,用于避免各線程的作用域被其他的侵入。 起步 到這已經能聲明簡單函數,返回靜態或者動態值了。定義INI選項,聲明內部數值或全局數值。本章節將介紹如何接收從調用腳本(php文件)傳入參數的數值,以及 PHP內核...
摘要:操作數本身并無數據類型,它的數據類型由操作碼確定任何架構的計算機都會對外提供指令集合運算器通過執行指令直接發出控制信號控制計算機各項操作。 順風車運營研發團隊 李樂 1.從物理機說起 虛擬機也是計算機,設計思想和物理機有很多相似之處; 1.1馮諾依曼體系結構 馮·諾依曼是當之無愧的數字計算機之父,當前計算機都采用的是馮諾依曼體系結構;設計思想主要包含以下幾個方面: 指令和數據不加區別...
摘要:中的用于類型整型和資源類型用于浮點類型用于字符串用于數組用于對象用于常量表達式才有多數文章,在提到變量結構體的時候,都提到,實際上這個論述并不準確,在為時,這個結果是正確的。主要看中的,是兩個,這個永遠是個字節,所以,因此。 PHP5 中的 zval // 1. zval typedef struct _zval_struct { zvalue_value value; ...
閱讀 2871·2021-10-14 09:43
閱讀 1657·2021-09-29 09:34
閱讀 1743·2021-07-28 00:16
閱讀 2962·2019-08-30 15:53
閱讀 2905·2019-08-30 13:59
閱讀 2961·2019-08-30 13:57
閱讀 1091·2019-08-26 13:38
閱讀 1893·2019-08-26 13:25