变量赋值 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;
}

变量赋值 php内核的实现(一),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。