• 关于PHP中的opcode


    简介

    1、当Zend engine解释器完成对脚本代码的分析后,便将它们生成可以直接运行的中间代码,也称为操作码(Operate Code,opcode),opcode是一个四元组,(opcode, op1, op2, result),它们分别代表操作码,第一操作数,第二操作数,结果

    2、因为PHP是构建在Zend虚拟机(Zend VM)之上的,所以PHP的opcode就是Zend虚拟机中的指令

    opcode结构

    struct _zend_op {
            opcode_handler_t handler;
            znode_op op1;
            znode_op op2;
            znode_op result;
            ulong extended_value;
            uint lineno;
            zend_uchar opcode;
            zend_uchar op1_type;
            zend_uchar op2_type;
            zend_uchar result_type;
    };
    

    1、 opcode_handler_t  opcode的函数指针 参考地址 opcode handler

    2、result

    我们看一下两个输出函数 ,echo 和 print

    void zend_do_print(znode *result, const znode *arg TSRMLS_DC) /* {{{ */
    {       
            zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
            
            opline->result_type = IS_TMP_VAR;
            opline->result.var = get_temporary_variable(CG(active_op_array));
            opline->opcode = ZEND_PRINT;
            SET_NODE(opline->op1, arg);
            SET_UNUSED(opline->op2);
            GET_NODE(result, opline->result);
    }
    /* }}} */
    
    void zend_do_echo(const znode *arg TSRMLS_DC) /* {{{ */
    {
            zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
    
            opline->opcode = ZEND_ECHO;
            SET_NODE(opline->op1, arg);
            SET_UNUSED(opline->op2);
    }
    /* }}} */
    

     我们可以看到print有result的设置 op2均为使用,从这里我们也能看出print和echo的区别来,print有返回值,而echo没有,这里的没有和返回null是不同的, 如果尝试将echo的值赋值给某个变量或者传递给函数都会出现语法错误

    3、op1,op2 记录参数

    4、lineno 对应的行号

    5、opcode 对应相应的操作

    6、extended_value   和CPU的指令类似,有一个标示指令的opcode字段,以及这个opcode所操作的操作数,PHP不像汇编那么底层, 在脚本实际执行的时候可能还需要其他更多的信息,extended_value字段就保存了这类信息

    关于result op1 op2的结构znode

    typedef union _znode_op {
            zend_uint      constant;
            zend_uint      var;
            zend_uint      num;
            zend_ulong     hash;
            zend_uint      opline_num; /*  Needs to be signed */
            zend_op       *jmp_addr;
            zval          *zv;
            zend_literal  *literal;
            void          *ptr;        /* Used for passing pointers from the compile to execution phase, currently used for traits */
    } znode_op;
    
    typedef struct _znode { /* used only during compilation */
            int op_type;
            union {
                    znode_op op;
                    zval constant; /* replaced by literal/zv */
                    zend_op_array *op_array;
                    zend_ast *ast;
            } u;
            zend_uint EA;      /* extended attributes */
    } znode;
    

     op_type

    #define IS_CONST    (1<<0)  
    #define IS_TMP_VAR  (1<<1)  
    #define IS_VAR      (1<<2)  
    #define IS_UNUSED   (1<<3)    /* Unused variable */  
    #define IS_CV       (1<<4)    /* Compiled variable */
    

    IS_CONST:表示常量,例如$a = 1; $b = "hello";这些代码生成OP后,1和"hello"都是以常量类型操作数存在。
    IS_TMP_VAR:表示临时变量,临时变量一般在前面加~来表示,这是一些OP执行过程中需要用到的中间变量,例如初始化一个数组的时候,就需要一个临时变量来暂时存储数组zval,然后将数组赋值给变量。
    IS_VAR: 一般意义上的变量,以$开发表示
    IS_UNUSED : 暂时不介绍,从名字来看应该是标识为不使用
    IS_CV:这种类型的操作数比较重要,此类型是在PHP后来的版本中(大概5.1)中才出现,CV的意思是compiled variable,即编译后的变量,变量都是保存在一个符号表中,这个符号表是一个哈希表,试想如果每次读写变量的时候都需要到哈希表中去检索,势必会对效率有一定的影响,因此在执行上下文环境中,会将一些编译期间生成的变量缓存起来,此过程以后再详细介绍。此类型操作数一般以!开头表示,比如变量$a=123;$b="hello"这段代码,$a和$b对应的操作数可能就是!0和!1, 0和1相当于一个索引号,通过索引号从缓存中取得相应的值。

    u

    此字段为一个联合体,根据op_type的不同,u取不同的值。比如op_type=IS_CONST的时候,u中的constant保存的就是操作数对 应的zval结构。例如$a=123时,123这个操作数中,u中的constant是一个IS_LONG类型的zval,其值lval为123

  • 相关阅读:
    【转】使用SpringMVC创建Web工程并使用SpringSecurity进行权限控制的详细配置方法
    配置Linux系统ssh免密登录
    numpy的随机数组
    numpy.where和numpy.piecewise的用法
    numpy、pandas学习笔记
    数据库行存储和列存储的区别
    pandas对DataFrame对象的基本操作
    pandas中assign方法的使用
    numpy实现快速傅里叶变换
    最小二乘法在线性拟合中的使用
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/4822672.html
Copyright © 2020-2023  润新知