• 深入PHP内核之opcode handler


    1、opcode结构

    在Zend/zend_compile.h文件下

    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;
    };
    

    opcode_handler_t是函数指针为opcode定义了执行方式,每一种opcode都对应一个的handler,也就是函数的入口地址,这些地址都保存在labels数组里面

    比如赋值:$a = 1

    通过vld可以看到

    op = ASSIGN 那么对应到zend engine  操作为ZEND_ASSIGN ,对应的编号为38的  可以在Zend/zend_vm_opcodes.h中查到定义  

    #define ZEND_ASSIGN                           38
    

    可以推算出 op_type为const和cv,然后就能确定handler为函数ZEND_ASSIGN_SPEC_CV_CONST_HANDLER

    然后就可以定位到具体的执行函数了

    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;
    	……
    }
    

     zend engine在执行的是首先会调用 zend_startup() 位于(zend.c中),然后初始化zend_init_opcodes_handlers()

    int zend_startup(zend_utility_functions *utility_functions, char **extensions TSRMLS_DC) /* {{{ */
    {
    #ifdef ZTS
            zend_compiler_globals *compiler_globals;
            zend_executor_globals *executor_globals;
            extern ZEND_API ts_rsrc_id ini_scanner_globals_id;
            extern ZEND_API ts_rsrc_id language_scanner_globals_id;
    #else
            extern zend_ini_scanner_globals ini_scanner_globals;
            extern zend_php_scanner_globals language_scanner_globals;
    #endif
    
            start_memory_manager(TSRMLS_C);
    
            virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */
    
    #if defined(__FreeBSD__) || defined(__DragonFly__)
            /* FreeBSD and DragonFly floating point precision fix */
            fpsetmask(0);
    #endif
    
            zend_startup_strtod();
            zend_startup_extensions_mechanism();
    
            /* Set up utility functions and values */
            zend_error_cb = utility_functions->error_function;
            zend_printf = utility_functions->printf_function;
            zend_write = (zend_write_func_t) utility_functions->write_function;
            zend_fopen = utility_functions->fopen_function;
            if (!zend_fopen) {
                    zend_fopen = zend_fopen_wrapper;
            }
            zend_stream_open_function = utility_functions->stream_open_function;
            zend_message_dispatcher_p = utility_functions->message_handler;
    #ifndef ZEND_SIGNALS
            zend_block_interruptions = utility_functions->block_interruptions;
            zend_unblock_interruptions = utility_functions->unblock_interruptions;
    #endif
            zend_get_configuration_directive_p = utility_functions->get_configuration_directive;
            zend_ticks_function = utility_functions->ticks_function;
            zend_on_timeout = utility_functions->on_timeout;
            zend_unblock_interruptions = utility_functions->unblock_interruptions;
    #endif
            zend_get_configuration_directive_p = utility_functions->get_configuration_directive;
            zend_ticks_function = utility_functions->ticks_function;
            zend_on_timeout = utility_functions->on_timeout;
            zend_vspprintf = utility_functions->vspprintf_function;
            zend_getenv = utility_functions->getenv_function;
            zend_resolve_path = utility_functions->resolve_path_function;
    
    #if HAVE_DTRACE
    /* build with dtrace support */
            zend_compile_file = dtrace_compile_file;
            zend_execute_ex = dtrace_execute_ex;
            zend_execute_internal = dtrace_execute_internal;
    #else
            zend_compile_file = compile_file;
            zend_execute_ex = execute_ex;
            zend_execute_internal = NULL;
    #endif /* HAVE_SYS_SDT_H */
            zend_compile_string = compile_string;
            zend_throw_exception_hook = NULL;
    
            zend_init_opcodes_handlers();
    	……
    }
     

    然后会初始化labels数组,这个数组的类型是opcode_handler_t (zend_compile.h)

    void zend_init_opcodes_handlers(void)
    {
      static const opcode_handler_t labels[] = {
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
            ZEND_NOP_SPEC_HANDLER,
        ……
        }
        zend_opcode_handlers = (opcode_handler_t*)labels;
    }
    //Zend/zend_vm_execute.h
    

     opcode_handler_t

    typedef int (*user_opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
    typedef int (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
    
    extern ZEND_API opcode_handler_t *zend_opcode_handlers;
    

    这个结构体包含了近4000个成员,这些成员都是函数名称,每次执行一个opcode的时候都要到这个labels里面找对应的handler处理函数

    op_type函数

    static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)
    {               
                    static const int zend_vm_decode[] = {
                            _UNUSED_CODE, /* 0              */
                            _CONST_CODE,  /* 1 = IS_CONST   */
                            _TMP_CODE,    /* 2 = IS_TMP_VAR */
                            _UNUSED_CODE, /* 3              */
                            _VAR_CODE,    /* 4 = IS_VAR     */
                            _UNUSED_CODE, /* 5              */
                            _UNUSED_CODE, /* 6              */
                            _UNUSED_CODE, /* 7              */
                            _UNUSED_CODE, /* 8 = IS_UNUSED  */
                            _UNUSED_CODE, /* 9              */
                            _UNUSED_CODE, /* 10             */
                            _UNUSED_CODE, /* 11             */
                            _UNUSED_CODE, /* 12             */
                            _UNUSED_CODE, /* 13             */
                            _UNUSED_CODE, /* 14             */
                            _UNUSED_CODE, /* 15             */
                            _CV_CODE      /* 16 = IS_CV     */
                    };
                    return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1_type] * 5 + zend_vm_decode[op->op2_type]];
    }
    
    ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
    {
            op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
    }
    

    返回对应的handler

    zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
    
  • 相关阅读:
    使用 ASP.NET Core 创建 Web API及链接sqlserver数据库
    SQLPrompt 最新版下载地址
    雷柏键鼠对码程序
    罗技无线鼠标接收器无法配对的详细解决办法
    Windows10安装NTP服务器
    查看udp端口及占用程序
    国人开发的api测试工具 ApiPost
    无法调用到appcode下的类
    Android 实时文件夹
    android 当ListView滚动时自动调用 onCheckedChanged 导致CheckBox 状态不停变化 的解决办法
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/4822614.html
Copyright © 2020-2023  润新知