变量赋值 php内核的实现(二)

<?php
$a=1;
$b=&$a;
$c=2;
$a=&$c;
echo $a."\n";
echo $b;

2

1

结论:

首先保存 左值的内存地址, 因这个内存地址会被再次被赋值

1)右值是引用

  进入2.2 2.3 2.4步骤

  例子:

<?php
  $a=1;  $c=2;
  $b=&$a;  //执行到这里时,属于第2种情况
  $c=&$a;  //执行到这里时,属于第1种情况,

2)右值不是引用,右值的refcount_gc减1

  2.1)如果refcount_gc减1,大于0 ,说明有别的变量也共同使用了zval,需要单独分配内存给右值

  2.2)将右值(内存地址)赋值给左值

  2.3)refcount_gc 加1,并设置 is_ref=1 

  2.4)销毁左值

    2.3.1)将上面保存的左值的zval的refcount_gc减1

      2.3.1.1)上面值为0,则zval_dtor

      2.3.1.2)上面值大于0,则进入GC buffer, 但zval类型必须为 object或 array

  

可以发现$a,$b,$c全是CV变量

当php解释器执行到$b=&$a时,会执行到下面的handler

static int ZEND_FASTCALL  ZEND_ASSIGN_REF_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    USE_OPLINE
    zend_free_op free_op2;
    zval **variable_ptr_ptr;
    zval **value_ptr_ptr;

    SAVE_OPLINE();
    value_ptr_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op2.var TSRMLS_CC);

    if (IS_CV == IS_VAR &&
        value_ptr_ptr &&
        !Z_ISREF_PP(value_ptr_ptr) &&
        opline->extended_value == ZEND_RETURNS_FUNCTION &&
        !EX_T(opline->op2.var).var.fcall_returned_reference) {
        if (free_op2.var == NULL) {
            PZVAL_LOCK(*value_ptr_ptr); /* undo the effect of get_zval_ptr_ptr() */
        }
        zend_error(E_STRICT, "Only variables should be assigned by reference");
        if (UNEXPECTED(EG(exception) != NULL)) {

            HANDLE_EXCEPTION();
        }
        return ZEND_ASSIGN_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
    } else if (IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_NEW) {
        PZVAL_LOCK(*value_ptr_ptr);
    }
    if (IS_CV == IS_VAR && UNEXPECTED(EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr)) {
        zend_error_noreturn(E_ERROR, "Cannot assign by reference to overloaded object");
    }

    variable_ptr_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC);
    if ((IS_CV == IS_VAR && UNEXPECTED(value_ptr_ptr == NULL)) ||
        (IS_CV == IS_VAR && UNEXPECTED(variable_ptr_ptr == NULL))) {
        zend_error_noreturn(E_ERROR, "Cannot create references to/from string offsets nor overloaded objects");
    }
    zend_assign_to_variable_reference(variable_ptr_ptr, value_ptr_ptr TSRMLS_CC); //在这里执行分配的操作

    if (IS_CV == IS_VAR && opline->extended_value == ZEND_RETURNS_NEW) {
        Z_DELREF_PP(variable_ptr_ptr);
    }

    if (RETURN_VALUE_USED(opline)) {
        PZVAL_LOCK(*variable_ptr_ptr);
        AI_SET_PTR(&EX_T(opline->result.var), *variable_ptr_ptr);
    }

    CHECK_EXCEPTION();
    ZEND_VM_NEXT_OPCODE();
}
static void zend_assign_to_variable_reference(zval **variable_ptr_ptr, zval **value_ptr_ptr TSRMLS_DC)
{
    zval *variable_ptr = *variable_ptr_ptr;    //对于$b=&$a , variable_ptr存放的是$b对应的的值zval所对应的内存地址(这里并没有zval) , variable_ptr_ptr实现存放的是$b变量的地址, $b存放的是对应zval的内存地址
    zval *value_ptr = *value_ptr_ptr;       //对于$b=&$a , value_ptr为 $a的值1的地址,value_ptr_ptr存放的是$a变量的地址,$a存放的是1的内存地址

    if (variable_ptr == &EG(error_zval) || value_ptr == &EG(error_zval)) {
        variable_ptr_ptr = &EG(uninitialized_zval_ptr);
    } else if (variable_ptr != value_ptr) {
        if (!PZVAL_IS_REF(value_ptr)) { //此时右值不是一个引用
            /* break it away */
            Z_DELREF_P(value_ptr);  //refcount_gc减1 的作用 是看 是否还有其他变量也使用了valu_ptr_ptr对应的zval,如果有,则重新分配zval
            if (Z_REFCOUNT_P(value_ptr)>0) {
                ALLOC_ZVAL(*value_ptr_ptr);
                ZVAL_COPY_VALUE(*value_ptr_ptr, value_ptr);
                value_ptr = *value_ptr_ptr;
                zendi_zval_copy_ctor(*value_ptr);
            }
            Z_SET_REFCOUNT_P(value_ptr, 1);  //因为上面减1了,所以这里要加1,
            Z_SET_ISREF_P(value_ptr);//设置 is_ref为1
        }

        *variable_ptr_ptr = value_ptr; //将variable_ptr_ptr这个地址指针内容 为 1 的地址
        Z_ADDREF_P(value_ptr); //还要将 refcount_gc加1

        zval_ptr_dtor(&variable_ptr); //根据情况释放内存
    } else if (!Z_ISREF_P(variable_ptr)) {
        if (variable_ptr_ptr == value_ptr_ptr) {
            SEPARATE_ZVAL(variable_ptr_ptr);
        } else if (variable_ptr==&EG(uninitialized_zval)
            || Z_REFCOUNT_P(variable_ptr)>2) {
            /* we need to separate */
            Z_SET_REFCOUNT_P(variable_ptr, Z_REFCOUNT_P(variable_ptr) - 2);
            ALLOC_ZVAL(*variable_ptr_ptr);
            ZVAL_COPY_VALUE(*variable_ptr_ptr, variable_ptr);
            zval_copy_ctor(*variable_ptr_ptr);
            *value_ptr_ptr = *variable_ptr_ptr;
            Z_SET_REFCOUNT_PP(variable_ptr_ptr, 2);
        }
        Z_SET_ISREF_PP(variable_ptr_ptr);
    }
}
//zend_variables.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_API.h

#define CHECK_ZVAL_STRING_REL(z)     if (Z_STRVAL_P(z)[ Z_STRLEN_P(z) ] != ‘\0‘) { zend_error(E_WARNING, "String is not zero-terminated (%s) (source: %s:%d)", Z_STRVAL_P(z) ZEND_FILE_LINE_RELAY_CC); }
//zend_alloc.h

#define estrndup_rel(s, length)                    _estrndup((s), (length) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC)
//zend_alloc.c

ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
    char *p;
#ifdef ZEND_SIGNALS
    TSRMLS_FETCH();
#endif

    HANDLE_BLOCK_INTERRUPTIONS();

    p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
    if (UNEXPECTED(p == NULL)) {
        HANDLE_UNBLOCK_INTERRUPTIONS();
        return p;
    }
    memcpy(p, s, length);
    p[length] = 0;
    HANDLE_UNBLOCK_INTERRUPTIONS();
    return p;
}
//zend_execute_API.cZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */
{
#if DEBUG_ZEND>=2
    printf("Reducing refcount for %x (%x): %d->%d\n", *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) - 1);
#endif
    Z_DELREF_PP(zval_ptr);
    if (Z_REFCOUNT_PP(zval_ptr) == 0) {
        TSRMLS_FETCH();

        if (*zval_ptr != &EG(uninitialized_zval)) {
            GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);
            zval_dtor(*zval_ptr);
            efree_rel(*zval_ptr);
        }
    } else {
        TSRMLS_FETCH();

        if (Z_REFCOUNT_PP(zval_ptr) == 1) {
            Z_UNSET_ISREF_PP(zval_ptr);
        }

        GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);
    }
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
    char *variable="abc";
    char **variable_ptr_ptr=variable;

    char *variable_ptr=*variable_ptr_ptr;

    char *value="def";
    char **value_ptr_ptr=value;
    char *value_ptr=*value_ptr_ptr;

    *variable_ptr_ptr=value_ptr;
    return 0;
}

变量赋值 php内核的实现(二)

时间: 2024-08-27 18:19:36

变量赋值 php内核的实现(二)的相关文章

变量赋值 php内核的实现(一)

<?php $name="abc"; $name="def"; 第二行代码运行后,"abc"去哪里了?明显被内存回收了,不是退回给OS,而是退回了当初分配给PHP的一大块内存区域;第一行中的$name哪儿去了,也被删除了 验证结论: 将常量赋值给某变量,内核会大致进行以下几个步骤: 1:将此变量的refcounf_gc减1 2:将此变量放入GC buffer中,当回收垃圾条件成熟时,回收内存 3:从php启动时已获取的一大片内存中为该变量分

变量赋值(非引用) php内核的实现(三)

<?php $a=1; $b=&a; $c=2; $d=$c; $c=$b; 结论: 保存左值的指针,为内存回收做准备,同时该指针会被再次赋值 1)左值不是一个引用 1.1)如果左值 refcount_gc为1,说明左值被赋过值, 1.1.1)右值为引用 ,进入第2步 1.1.2)右值不是引用,refcount_gc加1,将右值拷贝给左值 1.2)如果不为1,说明第一次出现,或者被别的变量共同使用了 zval, 其refcount_gc减1 ,将左值GC buffer中 (由GC判断是否需要

shell 的特性:命令行展开,变量赋值,以及文件层次标准(history)

bash默认会保存以前执行过的命令列表 当前shell的进程保存在缓冲区中 缓冲区中的命令会在shell退出后保存在文件中    .bash history 使用上下箭头可以翻看此前执行过的命令 history: !数字  :  执行history中第数字条命令 !!   :  上一条命令 !-数字:表示执行命令列表中的倒数第数字条命令 ESC .  : 执行上个命令的最后参数 ! string:执行命令列表中最后一个以string开头的命令 - d :删除第数字条记录 -c:清空history

.net开发环境的认识,控制台程序的创建,输出,输入,定义变量,变量赋值,值覆盖,值拼接,值打印 两种数据类型,整形类型转换

首先感谢向立凯老师带我走进.net这个很好的软件开发程序.通过两天的学习在这个领域的了解也多了很多,让大家先了解一下c#语言 c#是一种全新且简单.安全.快捷面向对象的程序设计语言.是专门为.net应用开发的语言..net的开发都基于一个统一的开发环境 Visual Studio.net.下面我们来看看怎么在Visual Studio.net.下创建以新的项目: 1.打开 2.新建一个项目 通过执行文件-新建-项目菜单命令,会弹出一个新建项目对话框.在这个对话框中选择开发语言为c#,选择框架版本

Linux内核分析(二)----内核模块简介|简单内核模块实现

Linux内核分析(二) 昨天我们开始了内核的分析,网上有很多人是用用源码直接分析,这样造成的问题是,大家觉得很枯燥很难理解,从某种意义上来说linux系统本身就是由一个个模块构成的,所以我会结合内核模块的设计,去分析内核,从而达到对linux内核的理解. 今天我们会分析到以下内容: 1.      Linux内核模块简介 2.      简单内核模块实现 l  Linux内核模块简介 1.       何为内核模块 在上一篇博文中我们先通过内核配置,在配置的过程中我们对内核的组件进行了选择(当

变量赋值和销毁

变量赋值有两种: 1:传值赋值, 先看图:如下 变量名存放处,可以看成一个体,装备 值和类型  又是一个个体, 每个变量名对应着一个值和一个隐属性(类型)  你值改成什么,名不会有影响 .一样,你名改什么,也影响不了别人.  这种赋值,叫传值赋值. 来看代码: $a=23; $b=20; $b=$a; var_dump($a,$b);  //代码分析: 要打印出变量 $a,$b,  其值为?? ,先看$a,值是23, 然后看$b其值是个变量$a,那个计算下,$a值是23,然后23再赋值给$b .

python变量赋值与运算符

目录:   1.变量赋值 [=]   2.输入输出[input raw_input,print]   3.常量   4.常用数据类型 [整数,浮点数,布尔值,空值]   5.字符串格式化 [%s,%d,%f,%x]   8.编码 [ASCII,unicode,UTF-8]   9.注释 [#,''']   10.python算术运算符 [+,-,*,/,//,%,**]   11.python比较运算符 [==,!=,<>,>,<,>=,<=]   12.python赋

sqlserver变量赋值介绍说明

变量对于一种语言是必不可少的一部分,当然,对于T-SQL来讲也是一样.在简单查询中,往往很少用到变量, 但无论对于复杂的查询或存储过程中,变量都是必不可少的一部分.在SQLServer中,变量分全局变量和局部变量两种: 一.全局变量: 全局变量是由系统定义的,在整个SQL Server实例内都能访问到的变量.全局变量以两个@@符号作为开头,用户只能访问,不能赋值. Sql Server一共提供了30多个全局变量 常用:@@IDENTITY  : 返回最后插入行的标识列的列值.@@ERROR  :

一天一点MySQL复习——获取数据库系统时间、变量赋值、变量比较

一.SQL获取系统时间 1 mysql> select now() from dual; 2 +---------------------+ 3 | now() | 4 +---------------------+ 5 | 2016-05-24 08:34:12 | 6 +---------------------+ 7 1 row in set (0.00 sec) 8 mysql> select curdate() from dual; 9 +------------+ 10 | cur