工做+实习快一年了,搞php后端开发,一直很迷茫怎么提升本身,就先从php源码开始吧,本人比较菜,本文章写的比较赶时间,因此有什么错误或者漏掉的地方,望各位大神指正,多交流才能成长嘛,嘿嘿。
本文主要是针对php7,php5的话能够移步到庆哥的博客看,还有就是小菜我读的是《php7内核剖析》这本书。
接下来我会使用到xdebug来调试php源码php
本文有参照ohmygirl博客中的部份内容以及代码。html
本文所用环境为windows,php7.0.10node
php7和php5不一样的地方有不少,zval,zend_value结构就是其中之一git
在php7中
zval定义在zend_types.h中github
在zval这个结构体重包含三个部分 zend_value(存储实际的内容),u1,u2两个联合体,其中u1主要存储变量相关的一下属性,而u2则是对u1的一些补充,例如当用到数组的时候,会用到u2.next来解决key哈希后出现的hash冲突segmentfault
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: 如下为外部使用的变量类型windows
#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中对与变量的实现分如下几种方式
(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中值传递不只仅那么简单,还有不少很复杂的东西,小菜我也是才看没多久,望各位大牛勿喷
写时复制是一种很重要的优化手段,这里涉及到深拷贝和浅拷贝的知识,请移步。
关于深拷贝,刚刚上面的这个例子就是很好的说明
所谓深拷贝就是将原有的数据拷贝一份放到独立分配一个地址空间。
而写时赋值就是对深拷贝的一种优化吧,意思是只有当发生写操做的时候才进行深拷贝
举个例子:
$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...
可能有遗漏的参考博客,望博主见谅