变量赋值 php内核的实现(一)
<?php $name="abc"; $name="def";
第二行代码运行后,"abc"去哪里了?明显被内存回收了,不是退回给OS,而是退回了当初分配给PHP的一大块内存区域;第一行中的$name哪儿去了,也被删除了
验证结论:
将常量赋值给某变量,内核会大致进行以下几个步骤:
1:将此变量的refcounf_gc减1
2:将此变量放入GC buffer中,当回收垃圾条件成熟时,回收内存
3:从php启动时已获取的一大片内存中为该变量分配一些内存
4:初始化此内存,例如赋值,拷贝构造函数
5: 将变量name 和 def所对应的val放入 symbol_table 符号表中, 没找相应代码
使用php 为变量赋值很简单,但里面的实现颇为复杂,在这里只对一部分代码给出了注释
通过VLD,可定位具体的分配函数 ZEND_ASSIGN_SPEC_CV_CONST_HANDLER
在编写调试代码时,把fprinf写成了sprintf,结果一直没有显示调试信息,一度怀疑不是ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,折腾了近2个小时 。。。。。
static int ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *value; zval **variable_ptr_ptr; SAVE_OPLINE(); value = opline->op2.zv; variable_ptr_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); if (IS_CV == IS_VAR && UNEXPECTED(variable_ptr_ptr == NULL)) { //这一步肯定不会被执行,不知为什么这么写 if (zend_assign_to_string_offset(&EX_T(opline->op1.var), value, IS_CONST TSRMLS_CC)) { if (RETURN_VALUE_USED(opline)) { zval *retval; ALLOC_ZVAL(retval); ZVAL_STRINGL(retval, Z_STRVAL_P(EX_T(opline->op1.var).str_offset.str)+EX_T(opline->op1.var).str_offset.offset, 1, 1); INIT_PZVAL(retval); AI_SET_PTR(&EX_T(opline->result.var), retval); } } else if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(&EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); } } else if (IS_CV == IS_VAR && UNEXPECTED(*variable_ptr_ptr == &EG(error_zval))) { if (0) { zval_dtor(value); } if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(&EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); } } else { if (IS_CONST == IS_TMP_VAR) { value = zend_assign_tmp_to_variable(variable_ptr_ptr, value TSRMLS_CC); } else if (IS_CONST == IS_CONST) { value = zend_assign_const_to_variable(variable_ptr_ptr, value TSRMLS_CC); //$name="def" 执行到这里 } else { value = zend_assign_to_variable(variable_ptr_ptr, value TSRMLS_CC); } if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(value); AI_SET_PTR(&EX_T(opline->result.var), value); } } /* zend_assign_to_variable() always takes care of op2, never free it! */ CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
//zend_execute.c static inline zval* zend_assign_const_to_variable(zval **variable_ptr_ptr, zval *value TSRMLS_DC) { zval *variable_ptr = *variable_ptr_ptr; zval garbage; /** *针对对象的赋值,以后再分析 */ if (Z_TYPE_P(variable_ptr) == IS_OBJECT && UNEXPECTED(Z_OBJ_HANDLER_P(variable_ptr, set) != NULL)) { Z_OBJ_HANDLER_P(variable_ptr, set)(variable_ptr_ptr, value TSRMLS_CC); return variable_ptr; } if (UNEXPECTED(Z_REFCOUNT_P(variable_ptr) > 1) && //左值的引用个数为1或0 AND 不是引用类型 EXPECTED(!PZVAL_IS_REF(variable_ptr))) { //左值不是个引用,而且可能多个变量共同使用左值的那个zval地址 /* we need to split */ Z_DELREF_P(variable_ptr); //将refcount_gc减1 GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr); //放到gc root缓冲区中 ALLOC_ZVAL(variable_ptr); //为左值 分配内存 INIT_PZVAL_COPY(variable_ptr, value); zval_copy_ctor(variable_ptr); *variable_ptr_ptr = variable_ptr; return variable_ptr; } else { if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) { /* nothing to destroy */ ZVAL_COPY_VALUE(variable_ptr, value); zendi_zval_copy_ctor(*variable_ptr); } else { ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_COPY_VALUE(variable_ptr, value); //这段代码执行前,value在内存中还不存在,所以要拷贝 zendi_zval_copy_ctor(*variable_ptr); _zval_dtor_func(&garbage ZEND_FILE_LINE_CC); } return variable_ptr; } }
#define ZVAL_COPY_VALUE(z, v) do { (z)->value = (v)->value; Z_TYPE_P(z) = Z_TYPE_P(v); } while (0) #define INIT_PZVAL_COPY(z, v) do { ZVAL_COPY_VALUE(z, v); Z_SET_REFCOUNT_P(z, 1); Z_UNSET_ISREF_P(z); } while (0) #define Z_SET_REFCOUNT_PP(ppz, rc) Z_SET_REFCOUNT_P(*(ppz), rc) #define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc) static zend_always_inline zend_uint zval_set_refcount_p(zval* pz, zend_uint rc) { return pz->refcount__gc = rc; } #define Z_UNSET_ISREF_PP(ppz) Z_UNSET_ISREF_P(*(ppz)) #define Z_UNSET_ISREF_P(pz) zval_unset_isref_p(pz) static zend_always_inline zend_bool zval_unset_isref_p(zval* pz) { return pz->is_ref__gc = 0; }
#define Z_DELREF_P(pz) (--(pz)->refcount)
#define zval_copy_ctor(zvalue) _zval_copy_ctor((zvalue) ZEND_FILE_LINE_CC)
ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC);
//zend_variable.c ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC) { switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) { case IS_RESOURCE: { TSRMLS_FETCH(); zend_list_addref(zvalue->value.lval); } break; case IS_BOOL: case IS_LONG: case IS_NULL: break; case IS_CONSTANT: case IS_STRING: CHECK_ZVAL_STRING_REL(zvalue); if (!IS_INTERNED(zvalue->value.str.val)) { zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len); } break; case IS_ARRAY: case IS_CONSTANT_ARRAY: { zval *tmp; HashTable *original_ht = zvalue->value.ht; HashTable *tmp_ht = NULL; TSRMLS_FETCH(); if (zvalue->value.ht == &EG(symbol_table)) { return; /* do nothing */ } ALLOC_HASHTABLE_REL(tmp_ht); zend_hash_init(tmp_ht, zend_hash_num_elements(original_ht), NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(tmp_ht, original_ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); zvalue->value.ht = tmp_ht; } break; case IS_OBJECT: { TSRMLS_FETCH(); Z_OBJ_HT_P(zvalue)->add_ref(zvalue TSRMLS_CC); } break; } }
//zend.c static zend_bool php_auto_globals_create_globals(const char *name, uint name_len TSRMLS_DC) /* {{{ */ { zval *globals; ALLOC_ZVAL(globals); Z_SET_REFCOUNT_P(globals, 1); Z_SET_ISREF_P(globals); Z_TYPE_P(globals) = IS_ARRAY; Z_ARRVAL_P(globals) = &EG(symbol_table); zend_hash_update(&EG(symbol_table), name, name_len + 1, &globals, sizeof(zval *), NULL); return 0; } static zend_bool php_auto_globals_create_globals(const char *name, uint name_len TSRMLS_DC) /* {{{ */ { zval *globals; ALLOC_ZVAL(globals); Z_SET_REFCOUNT_P(globals, 1); Z_SET_ISREF_P(globals); Z_TYPE_P(globals) = IS_ARRAY; Z_ARRVAL_P(globals) = &EG(symbol_table); zend_hash_update(&EG(symbol_table), name, name_len + 1, &globals, sizeof(zval *), NULL); return 0; } /* }}} */ int zend_startup(zend_utility_functions *utility_functions, char **extensions TSRMLS_DC) /* {{{ */ ....... zend_register_auto_global("GLOBALS", sizeof("GLOBALS") - 1, 1, php_auto_globals_create_globals TSRMLS_CC); ....... return SUCCESS; }
#define ZEND_SET_GLOBAL_VAR(name, var) \
ZEND_SET_SYMBOL(&EG(symbol_table), name, var)
//zend_execute.c #define ZEND_VM_NEXT_OPCODE() \ CHECK_SYMBOL_TABLES() ZEND_VM_INC_OPCODE(); ZEND_VM_CONTINUE() #define CHECK_SYMBOL_TABLES() \ zend_hash_apply(&EG(symbol_table), (apply_func_t) zend_check_symbol TSRMLS_CC); if (&EG(symbol_table)!=EG(active_symbol_table)) { zend_hash_apply(EG(active_symbol_table), (apply_func_t) zend_check_symbol TSRMLS_CC); } static int zend_check_symbol(zval **pz TSRMLS_DC) { if (Z_TYPE_PP(pz) > 9) { fprintf(stderr, "Warning! %x has invalid type!\n", *pz); /* See http://support.microsoft.com/kb/190351 */ #ifdef PHP_WIN32 fflush(stderr); #endif } else if (Z_TYPE_PP(pz) == IS_ARRAY) { zend_hash_apply(Z_ARRVAL_PP(pz), (apply_func_t) zend_check_symbol TSRMLS_CC); } else if (Z_TYPE_PP(pz) == IS_OBJECT) { /* OBJ-TBI - doesn‘t support new object model! */ zend_hash_apply(Z_OBJPROP_PP(pz), (apply_func_t) zend_check_symbol TSRMLS_CC); } return 0; }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。