PHP7 内核的基本变量

 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;
};
  1. nNumOfElements
    标识现在存储在数组里面的值的数量。这也是函数count的返回值
  2. nTableSize
    表示哈希表的容量。它通常是下一个大于等于nNumOfElements的2的幂值。比如,如果数组存储了32元素,那么哈希表也是32大小的容量。但如果再多一个元素添加进来,也就是说,数组现在有33个元素,那么哈希表的容量就被调整为64。 这是为了保持哈希表在空间和时间上始终有效。很明显,如果哈希表太小,那么将会有很多的冲突,而且性能也会降低。另一方面,如果哈希表太大,那么浪费内存。2的幂值是一个很好的折中方案。
  3. nTableMask
    是哈希表的容量减一。这个mask用来根据当前的表大小调整生成的哈希值。例如,”foo”真正的哈希值(使用DJBX33A哈希函数)是193491849。如果我们现在有64容量的哈希表,我们明显不能使用它作为数组的下标。取而代之的是通过应用哈希表的mask,然后只取哈希表的低位。
    hash | 193491849 | 0b1011100010000111001110001001
    & mask | & 63 | & 0b0000000000000000000000111111
    = index | = 9 | = 0b0000000000000000000000001001
  4. nNextFreeElement
    是下一个可以使用的数字键值,当你使用$array[] = “abc”时候被用到。
  5. pInternalPointer
    存储数组当前的位置。这个值在foreach遍历时可使用reset(),current(),key(),next(),prev()和end()函数访问。
  6. pListHead和pListTail
    标识了数组的第一个和最后一个元素的位置。记住:PHP的数组是有序集合。比如,[‘foo’ => ‘bar’, ‘bar’ => ‘foo’]和[‘bar’ => ‘foo’, ‘foo’ => ‘bar’]这两个数组包含了相同的元素,但却有不同的顺序。
  7. arBuckets
    是我们经常谈论的“哈希表(internal C array)”。它用Bucket **来定义,因此它可以被看作数组的bucket指针(我们会马上谈论Bucket是什么)。
  8. pDestructor
    是值的析构器。如果一个值从HT中移除,那么这个函数会被调用。常见的析构函数是zval_ptr_dtor。zval_ptr_dtor会减少zval的引用数量,而且,如果它遇到o,它会销毁和释放它。
  • 数组实现过程1:

  • 数组实现过程2:

  • 数组实现过程3:

  • 数组实现过程4:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值