PHP7 内核的基本变量

    xiaoxiao2024-12-09  60

     1.字节对齐

    1.1 好处:

    为了减少使用的内存为了提升数据读取的效率

    #pragma pack(1)  :可以让编译器按照1字节对齐

    1.2 结构体和联合体对齐:

    1.3   替换,在源码里我们经常看见这样的宏,逼格很高

    1.4 小而巧的zval

          php7和php5不同的地方有很多,zval,zend_value结构就是其中之一。在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; };

     1.4.1 php7中zval结构:

    1.4.2 php7中zend_value结构: 

    typedef union _zend_value { zend_long lval; //整形 double dval; //浮点型 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;

    1.4.3 php7中 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

    1.4.4 zend_value各种类型的构造:

    1.4.5 php中的COW(copy on write/写时复制)

    直接复制

    $a=10; $b=$a; 这个操作会使两个zval指向同一个zend_value 这里并没有触发COW,执行深拷贝

           当$b = "new string";发生了写操作的时候,触发COW,执行深拷贝,拷贝了完全一样的一份zend_value,$b所在的zval由原来的和$a所在的zval共同指向之前的zend_value, 转换成指向拷贝出的新的zend_value,如果不进行深拷贝的话,那么当执行$b= "new string";后,$a也会等于"new string"。具体过程如下:

    1.

    2.

    3. 

    4.

    1.5 PHP7数组的实现

    //Bucket:散列表中存储 typedef struct _Bucket { zval val; //存储的具体value,这里嵌入了一个zval,而不是一个指针 zend_ulong h; //key根据times 33计算得到的哈希值,或者是数值索引编号 zend_string *key; //存储元素的key } Bucket; //HashTable结构 typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar reserve) } v; uint32_t flags; } u; uint32_t nTableMask; //哈希值计算掩码,等于nTableSize的负值(nTableMask = -nTableSize) Bucket *arData; //存储元素数组,指向第一个Bucket uint32_t nNumUsed; //已用Bucket数 uint32_t nNumOfElements; //哈希表有效元素数 uint32_t nTableSize; //哈希表总大小,为2的n次方 uint32_t nInternalPointer; zend_long nNextFreeElement; 下一个可用的数值索引,如:arr[] = 1;arr["a"] = 2;arr[] = 3;则nNextFreeElement = 2; dtor_func_t pDestructor; }; nNumOfElements 标识现在存储在数组里面的值的数量。这也是函数count的返回值nTableSize 表示哈希表的容量。它通常是下一个大于等于nNumOfElements的2的幂值。比如,如果数组存储了32元素,那么哈希表也是32大小的容量。但如果再多一个元素添加进来,也就是说,数组现在有33个元素,那么哈希表的容量就被调整为64。 这是为了保持哈希表在空间和时间上始终有效。很明显,如果哈希表太小,那么将会有很多的冲突,而且性能也会降低。另一方面,如果哈希表太大,那么浪费内存。2的幂值是一个很好的折中方案。nTableMask 是哈希表的容量减一。这个mask用来根据当前的表大小调整生成的哈希值。例如,”foo”真正的哈希值(使用DJBX33A哈希函数)是193491849。如果我们现在有64容量的哈希表,我们明显不能使用它作为数组的下标。取而代之的是通过应用哈希表的mask,然后只取哈希表的低位。 hash | 193491849 | 0b1011100010000111001110001001 & mask | & 63 | & 0b0000000000000000000000111111 = index | = 9 | = 0b0000000000000000000000001001nNextFreeElement 是下一个可以使用的数字键值,当你使用$array[] = “abc”时候被用到。pInternalPointer 存储数组当前的位置。这个值在foreach遍历时可使用reset(),current(),key(),next(),prev()和end()函数访问。pListHead和pListTail 标识了数组的第一个和最后一个元素的位置。记住:PHP的数组是有序集合。比如,[‘foo’ => ‘bar’, ‘bar’ => ‘foo’]和[‘bar’ => ‘foo’, ‘foo’ => ‘bar’]这两个数组包含了相同的元素,但却有不同的顺序。arBuckets 是我们经常谈论的“哈希表(internal C array)”。它用Bucket **来定义,因此它可以被看作数组的bucket指针(我们会马上谈论Bucket是什么)。pDestructor 是值的析构器。如果一个值从HT中移除,那么这个函数会被调用。常见的析构函数是zval_ptr_dtor。zval_ptr_dtor会减少zval的引用数量,而且,如果它遇到o,它会销毁和释放它。 数组实现过程1:

    数组实现过程2:

    数组实现过程3:

    数组实现过程4:

    最新回复(0)