摘要:本文主要是針對,的話可以移步到慶哥的博客看,還有就是小菜我讀的是內(nèi)核剖析這本書。接下來我會(huì)使用到來調(diào)試源碼本文有參照博客中的部分內(nèi)容以及代碼。
前言
工作+實(shí)習(xí)快一年了,搞php后端開發(fā),一直很迷茫怎么提高自己,就先從php源碼開始吧,本人比較菜,本文章寫的比較趕時(shí)間,所以有什么錯(cuò)誤或者漏掉的地方,望各位大神指正,多交流才能成長嘛,嘿嘿。
本文主要是針對php7,php5的話可以移步到慶哥的博客看,還有就是小菜我讀的是《php7內(nèi)核剖析》這本書。
接下來我會(huì)使用到xdebug來調(diào)試php源碼
本文有參照ohmygirl博客中的部分內(nèi)容以及代碼。
本文所用環(huán)境為windows,php7.0.10
php7中zval,zend_value的基本結(jié)構(gòu)php7和php5不同的地方有很多,zval,zend_value結(jié)構(gòu)就是其中之一
在php7中
zval定義在zend_types.h中
在zval這個(gè)結(jié)構(gòu)體重包含三個(gè)部分 zend_value(存儲(chǔ)實(shí)際的內(nèi)容),u1,u2兩個(gè)聯(lián)合體,其中u1主要存儲(chǔ)變量相關(guān)的一下屬性,而u2則是對u1的一些補(bǔ)充,例如當(dāng)用到數(shù)組的時(shí)候,會(huì)用到u2.next來解決key哈希后出現(xiàn)的hash沖突
struct _zval_struct { zend_value value; /* 存儲(chǔ)變量的實(shí)際內(nèi)容 */ union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* 存儲(chǔ)變量的類型 */ zend_uchar type_flags, /* 用于標(biāo)識(shí)變量狀態(tài),例如GC方面的管理,通過設(shè)置為IS_TYPE_COLLECTABLE 則變量會(huì)被收集到GC中回收垃圾的buffer緩存區(qū)中 */ 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結(jié)構(gòu)
typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; /*用于統(tǒng)計(jì)計(jì)數(shù)用,*/ 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中重要的的幾個(gè)變量
zval中:
(1)zend_uchar type 這個(gè)是用來表示當(dāng)前變量(例如 $a)是什么類型的變量($a是string類型?還是int類型?還是引用類型....)
zend_value中:
(1)zend_refcounted *counted; 表示引用計(jì)數(shù)的次數(shù), 何為引用計(jì)數(shù)? 就是zend_value變量被zval引用的次數(shù),例如我們$a=“abcs”;$c=$a;$b=$a;此時(shí)counted=3,如下圖,其中refcount也是實(shí)現(xiàn)GC自動(dòng)內(nèi)存回收的基礎(chǔ),下面會(huì)詳細(xì)講解php7變量的內(nèi)部實(shí)現(xiàn)
php7中對與變量的實(shí)現(xiàn)分以下幾種方式
(1).對于boolen類型,還有null,undefined,這種沒有具體值,只有類型的類型,直接在zval中通過zend_uchar type的類型來判斷,無需通過引用計(jì)數(shù)來實(shí)現(xiàn)。
正是因?yàn)闆]有通過應(yīng)用計(jì)數(shù)來實(shí)現(xiàn),所以它refcount為0
(2)對于int類型和float類型,因?yàn)樵趜end_value中有zend_long和double來保存數(shù)據(jù),如下圖,所以,在賦值的時(shí)候就不需要再使用引用計(jì)數(shù)了,在拷貝的直接進(jìn)行賦值就行了,這樣做可以省掉大量引用計(jì)數(shù)的相關(guān)操作
定義一個(gè)$a=1,在php內(nèi)核中zval和zend_value的關(guān)系
(3)第三種就是常規(guī)的使用引用計(jì)數(shù)的方式來進(jìn)行來進(jìn)行變量的定義。在這些變量的實(shí)現(xiàn)中,都是通過指針指向一個(gè)具體的數(shù)據(jù)類型
例如 :
zend_string *str; struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1]; };
$假設(shè)我定義一個(gè)$a="111";其內(nèi)部的實(shí)現(xiàn)就是,注意這里zend_string中char val[1],主要是因?yàn)镃語言中字符串是以“0”結(jié)尾的,所以在zend_string中$a="111"這個(gè)變量值的存儲(chǔ)是char val[4]="111"。
1.普通賦值
前面說到,在php中,定義一個(gè)變量$a="444",實(shí)際上是生成了一個(gè)zval,和一個(gè)zend_value,然后zval指向這個(gè)zend_value來實(shí)現(xiàn)對$a="444"的定義的,然后通過refcount來統(tǒng)計(jì)引用的次數(shù)。
**所以可以總結(jié)出,refcount表示當(dāng)前有多少個(gè)zval指向同一個(gè)zend_value**
我們定義如下:
$a="111";
$b=$a;
當(dāng)然對于像boole型還有int,double,null變量,他們的直接通過zval保存,不會(huì)公用一個(gè)zend_value,所以直接使用深拷貝。
至于什么是深拷貝,什么是淺拷貝,最直接的區(qū)別就是在于有沒有重新生成一個(gè)一模一樣的zend_value,詳細(xì)請參看深拷貝和淺拷貝。
后面的寫時(shí)賦值(COW copy on write)就會(huì)使用到深拷貝。
對于php的賦值,實(shí)際上并不是所有的類型都是一樣的,剛剛也有說到,在php的zval中就有一個(gè)專門的字段用于標(biāo)識(shí)當(dāng)前類型適合哪種形式的那就是。
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這個(gè)字段用于標(biāo)識(shí)當(dāng)前的zval的屬于哪種賦值方式或者處于哪種狀態(tài),主要是用于內(nèi)存方面的管理具體參見內(nèi)存管理,
2.引用賦值
php中的引用賦值就是我們常用的引用,例如$a=&$b,這樣。
在php7中實(shí)現(xiàn)引用的時(shí)候,在php中實(shí)現(xiàn)引用的時(shí)候,會(huì)先生成一個(gè)zend_reference類型,這個(gè)類型中嵌套一個(gè)zval,然后這個(gè)zval的zend_value會(huì)指向之前的之前的那個(gè)zval的zend_value,原來的那個(gè)zval類型轉(zhuǎn)換成IS_REFERENCE類型,簡單來說**就是將原有的zval類型轉(zhuǎn)換成IS_REFERENCE,并新生成zend_reference類型指向原zend的zend_value。(好jb繞,理了好久)。
struct _zend_reference { zend_refcounted_h gc; zval val; };
例如我們定義一個(gè)$a="1111";$b=&$a;它的變換如下圖
$a="111"
$b=&$a;
可能有朋友注意到了,在zend_reference中refcount為2 ,但是在最后的真實(shí)的zend_value為refcount為1,這是為什么呢,其實(shí)很好理解,前面說過,refcount表示有多少個(gè)zval指向該zend_value,zend_reference中只有一個(gè)zval指向了zend_value。
中間加一層zend_reference這樣做其實(shí)有很多好處,這樣可以保留原有的zend_value類型不變,為深拷貝操作提供拷貝條件,下面我們舉個(gè)例子就知道了
是不是一目了然,只能說開發(fā)內(nèi)核的那些神牛太牛逼了。。。。。。。
當(dāng)然關(guān)于php中值傳遞不僅僅那么簡單,還有很多很復(fù)雜的東西,小菜我也是才看沒多久,望各位大牛勿噴
php中的COW (copy on write)寫時(shí)復(fù)制是一種很重要的優(yōu)化手段,這里涉及到深拷貝和淺拷貝的知識(shí),請移步。
關(guān)于深拷貝,剛剛上面的這個(gè)例子就是很好的說明
所謂深拷貝就是將原有的數(shù)據(jù)拷貝一份放到獨(dú)立分配一個(gè)地址空間。
而寫時(shí)賦值就是對深拷貝的一種優(yōu)化吧,意思是只有當(dāng)發(fā)生寫操作的時(shí)候才進(jìn)行深拷貝
舉個(gè)例子:
$a="3333"; $b=$a; $b.="444";
$a="3333";
$b=$a;
這個(gè)操作會(huì)使兩個(gè)zval指向同一個(gè)zend_value
這里并沒有觸發(fā)COW,執(zhí)行深拷貝
當(dāng)$b.="444";發(fā)生了寫操作的時(shí)候,觸發(fā)COW,執(zhí)行深拷貝,拷貝了完全一樣的一份zend_value,$b所在的zval由原來的和$a所在的zval共同指向之前的zend_value, 轉(zhuǎn)換成指向拷貝出的新的zend_value
如果不進(jìn)行深拷貝的話,那么當(dāng)執(zhí)行$b.="444";后,$a也會(huì)等于“3333444”
當(dāng)然不是所有的zend_value類型都可以進(jìn)行復(fù)制,這個(gè)請參見我之前的那個(gè)zend_uchar type_flags表
**文章中可能有些不足的地方,懇求指正。
本來打算再寫寫GC回收的原理的,但是現(xiàn)在已經(jīng)2點(diǎn)多了,23333333.明天還要繼續(xù)打碼呢。。。。。。。不好意思**
下一篇準(zhǔn)備寫一下php中的數(shù)組的實(shí)現(xiàn);
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...
可能有遺漏的參考博客,望博主見諒
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/28175.html
摘要:垃圾回收所謂垃圾就是指通過循環(huán)引用自己引用自己,目前只在類型中有出現(xiàn)的形式而導(dǎo)致永遠(yuǎn)不為。當(dāng)出現(xiàn)垃圾之后,的引擎有對應(yīng)的垃圾回收機(jī)制。觸發(fā)這個(gè)機(jī)制的時(shí)機(jī)是每次出現(xiàn)減少時(shí)候。 自嘲)。。。。。2333,我覺得這是因?yàn)樵趐hp語言層面就幫我們解決了內(nèi)存回收的問題,但這讓我在和java大牛們吹牛逼的時(shí)候,聽到什么內(nèi)存泄露。。。。(納尼,我tmd怎么從來沒遇見過)一臉懵逼。 本人小菜,如果下面...
摘要:插入一個(gè)元素時(shí)先將元素按先后順序插入數(shù)組,位置是,再根據(jù)的哈希值映射到散列表中的某個(gè)位置,將存入這個(gè)位置查找時(shí)先在散列表中映射到,得到在數(shù)組的位置,再從數(shù)組中取出元素。目前只有兩種類型會(huì)使用這種機(jī)制。 1.變量結(jié)構(gòu) typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:告訴引擎要取的參數(shù)的信息,用來確保線程安全,返回值檢測是還是。數(shù)組遍歷假設(shè)我們需要一個(gè)取代以下功能的擴(kuò)展的遍歷數(shù)組和差很多,提供了一些專門的宏來遍歷元素或。是一個(gè)關(guān)于線程安全的動(dòng)作,用于避免各線程的作用域被其他的侵入。 起步 到這已經(jīng)能聲明簡單函數(shù),返回靜態(tài)或者動(dòng)態(tài)值了。定義INI選項(xiàng),聲明內(nèi)部數(shù)值或全局?jǐn)?shù)值。本章節(jié)將介紹如何接收從調(diào)用腳本(php文件)傳入?yún)?shù)的數(shù)值,以及 PHP內(nèi)核...
摘要:操作數(shù)本身并無數(shù)據(jù)類型,它的數(shù)據(jù)類型由操作碼確定任何架構(gòu)的計(jì)算機(jī)都會(huì)對外提供指令集合運(yùn)算器通過執(zhí)行指令直接發(fā)出控制信號(hào)控制計(jì)算機(jī)各項(xiàng)操作。 順風(fēng)車運(yùn)營研發(fā)團(tuán)隊(duì) 李樂 1.從物理機(jī)說起 虛擬機(jī)也是計(jì)算機(jī),設(shè)計(jì)思想和物理機(jī)有很多相似之處; 1.1馮諾依曼體系結(jié)構(gòu) 馮·諾依曼是當(dāng)之無愧的數(shù)字計(jì)算機(jī)之父,當(dāng)前計(jì)算機(jī)都采用的是馮諾依曼體系結(jié)構(gòu);設(shè)計(jì)思想主要包含以下幾個(gè)方面: 指令和數(shù)據(jù)不加區(qū)別...
摘要:中的用于類型整型和資源類型用于浮點(diǎn)類型用于字符串用于數(shù)組用于對象用于常量表達(dá)式才有多數(shù)文章,在提到變量結(jié)構(gòu)體的時(shí)候,都提到,實(shí)際上這個(gè)論述并不準(zhǔn)確,在為時(shí),這個(gè)結(jié)果是正確的。主要看中的,是兩個(gè),這個(gè)永遠(yuǎn)是個(gè)字節(jié),所以,因此。 PHP5 中的 zval // 1. zval typedef struct _zval_struct { zvalue_value value; ...
閱讀 2935·2021-10-14 09:43
閱讀 1780·2021-09-29 09:34
閱讀 1819·2021-07-28 00:16
閱讀 3015·2019-08-30 15:53
閱讀 2978·2019-08-30 13:59
閱讀 3025·2019-08-30 13:57
閱讀 1165·2019-08-26 13:38
閱讀 1955·2019-08-26 13:25