深入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]];
时间: 2024-09-29 08:58:39

深入PHP内核之opcode handler的相关文章

关于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 handle

请远离include_once和require_once[转]

来自:http://www.poluoluo.com/jzxy/201306/216921.html 尽量使用include, 而不是include_once, 理由是 include_once需要查询一遍已加载的文件列表, 确认是否存在, 然后再加载. 诚然, 这个理由是对的, 不过, 我今天要说的, 是另外一个的原因. 我们知道, PHP去判断一个文件是否被加载, 是需要得到这个文件的opened_path的, 意思是说, 比如: <?php    set_include_path("

[转载]A Crash Course on the Depths of Win32 Structured Exception Handling

转自:[已完工][经典文章翻译]A Crash Course on the Depths of Win32 Structured Exception Handling 原文题目: <<A Crash Course on the Depths of Win32™ Structured Exception Handling>> 原文地址: http://www.microsoft.com/msj/0197/Exception/Exception.aspx 原作者: Matt Pietr

【转载】Mini2440启动配置文件说明

对于mini2440,虽然root_qtopia这个文件系统的GUI是基于Qtopia的,但其初始化启动过程却是由大部分由busybox完成,Qtopia(qpe)只是在启动的最后阶段被开启. 由于默认的内核命令行上有init=/linuxrc, 因此,在文件系统被挂载后,运行的第一个程序是根目录下的linuxrc.这是一个指向/bin/busybox的链接,也就是说,系统起来后运行的第一 个程序也就是busybox本身.这种情况下,busybox首先将试图解析/etc/inittab来获取进一

深入解析php中的foreach问题

本篇文章是对php中的foreach问题进行了详细的分析介绍,需要的朋友参考下 前言:php4中引入了foreach结构,这是一种遍历数组的简单方式.相比传统的for循环,foreach能够更加便捷的获取键值对.在php5之前,foreach仅能用于数组:php5之后,利用foreach还能遍历对象(详见:遍历对象).本文中仅讨论遍历数组的情况. foreach虽然简单,不过它可能会出现一些意外的行为,特别是代码涉及引用的情况下.下面列举了几种case,有助于我们进一步认清foreach的本质.

Linux根文件皇冠体育平台开发系统:/etc/init.d/rcS文件分析

rcS文件的作用皇冠体育平台开发论坛:haozbbs.com Q1446595067 rcS是一个脚本文件,在inittab文件中本解析调用,用于配置Linux系统.2.rcS文件分析 #! /bin/sh #指定系统使用的shell PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin: #初始化环境变量PATH,操作系统执行程序默认到PATH指定的目录下寻找该程序 runlevel=S #设置系统运行级别为S,即单用户模式,只有一个控制台终端,供

PHP Opcode内核实现 - [ PHP内核学习 ]

catalogue 1. Opcode简介 2. PHP中的Opcode 3. opcode翻译执行(即时解释执行) 1. Opcode简介 opcode是计算机指令中的一部分,用于指定要执行的操作, 指令的格式和规范由处理器的指令规范指定. 除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显式的操作数. 这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等 通常opcode还有另一种称谓: 字节码(byte codes). 例如Java虚拟机(JVM),.

获取内核当前执行模块和当前发生异常地址和线程异常Handler调用步骤

循环每个内核模块 统计模块大小 判断触发异常的地址属于那个模块.来确定模块 获取发生异常地址 nt!_EXCEPTION_RECORD +0x000 ExceptionCode : -2139160568 +0x004 ExceptionFlags : 0x807fa938 +0x008 ExceptionRecord : 0x00010000 _EXCEPTION_RECORD +0x00c ExceptionAddress : 0x807fa340 +0x010 NumberParamete

SQLite内核研究

先从全局的角度把握SQLite内核各个模块的设计和功能.SQLite采用了层次化.模块化的设计,而这些使得它的可扩展性和可移植性非常强.而且SQLite的架构与通用DBMS的结构差别不是很大,所以它对于理解通用DBMS具有重要意义.SQLite的内核总的来说分为三个部分,虚拟机(Virtual Machine).Back-end(后端)和compiler(编译器). 1.虚拟机(Virtual Machine)VDBE是SQLite的核心,它的上层模块和下层模块都是本质上都是为它服务的.它的实现