PHP内核学习教程之php opcode内核实现
opcode是计算机指令中的一部分,用于指定要执行的操作, 指令的格式和规范由处理器的指令规范指定。 除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显式的操作数。 这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等。 通常opcode还有另一种称谓:字节码(byte codes)。 例如Java虚拟机(JVM),.NET的通用中间语言(CIL: Common Intermeditate Language)等等。 1. Opcode简介 opcode是计算机指令中的一部分,用于指定要执行的操作, 指令的格式和规范由处理器的指令规范指定。 除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显式的操作数。 这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等 通常opcode还有另一种称谓: 字节码(byte codes)。 例如Java虚拟机(JVM),.NET的通用中间语言(CIL: Common Intermeditate Language)等等 PHP中的opcode则属于前面介绍中的后着,PHP是构建在Zend虚拟机(Zend VM)之上的。PHP的opcode就是Zend虚拟机中的指令(基于Zend的中间代码) Relevant Link:
2. PHP中的Opcode 0x1: 数据结构在PHP实现内部,opcode由如下的结构体表示 php-5.6.17Zendzend_compile.h和CPU的指令类似,有一个标示指令的opcode字段,以及这个opcode所操作的操作数,PHP不像汇编那么底层, 在脚本实际执行的时候可能还需要其他更多的信息,extended_value字段就保存了这类信息, 其中的result域则是保存该指令执行完成后的结果 例如如下代码是在编译器遇到print语句的时候进行编译的函数 php-5.6.17Zendzend_compile.c//将新建的zend_op的返回值类型设置为临时变量(IS_TMP_VAR),因为print中的内存仅仅为了临时输出,并不需要保存
opline->result_type = IS_TMP_VAR; //为临时变量申请空间 opline->result.var = get_temporary_variable(CG(active_op_array)); //指定opcode为ZEND_PRINT opline->opcode = ZEND_PRINT; //将传递进来的参数赋值给这条opcode的第一个操作数 SET_NODE(opline->op1,arg); SET_UNUSED(opline->op2); GET_NODE(result,opline->result); } 0x2: opcode类型: zend_op->zend_uchar opcode比对汇编语言的概念,每个opcode都对应于一个类型,表明该opcpde的"操作指令",opcode的类型为zend_uchar,zend_uchar实际上就是unsigned char,此字段保存的整形值即为op的编号,用来区分不同的op类型,opcode的可取值都被定义成了宏 /Zend/zend_vm_opcodes.h0x3: opcode执行句柄: zend_op->handlerop的执行句柄,其类型为opcode_handler_t typedef int (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS); 这个函数指针为op定义了执行方式,每一种opcode字段都对应一个种类的handler,比如如果$a = 1;这样的代码生成的op,操作数为const和cv,最后就能确定handler为函数ZEND_ASSIGN_SPEC_CV_CONST_HANDLER /Zend/zend_vm_execute.h0x4: opcpde操作数znode操作数字段是_zend_op类型中比较重要的部分了,其中op1,op2,result三个操作数定义为znode类型 php-5.6.17Zendzend_compile.h0x5: opcode编译后数组op_array在zend_do_print函数中的第一行,我们注意到下面这行代码 PHP脚本代码被编译后产生的opcode保存在op_array中,其内部存储的结构如下 php-5.6.17Zendzend_compile.h整个PHP脚本代码被编译后的opcodes保存在这里,在执行的时候由下面的execute函数执行 每条opcode都有一个opcode_handler_t的函数指针字段,用于执行该opcode,PHP有三种方式来进行opcode的处理 1. CALL: PHP默认使用CALL的方式,也就是函数调用的方式 2. SWITCH: 由于opcode执行是每个PHP程序频繁需要进行的操作,可以使用SWITCH或者GOTO的方式来分发 3. GOTO: 通常GOTO的效率相对会高一些,不过效率是否提高依赖于不同的CPU 实际上我们会发现,在/zend/zend_language_parser.c中就是Zend的opcode翻译解释执行过程,其中包含了call、switch、goto三种opcode执行方式 这就是PHP为什么称之为解释型语言的内核原理,PHP在完成Lex词法解析后,在语法解析即生成产生式的时候,直接通过call、switch、goto的方式调用zend api进行即使解释执行 Relevant Link:3. opcode翻译执行(即时解释执行) Relevant Link:以上所述本文给大家介绍的PHP内核学习教程之php opcode内核实现的相关知识,希望对大家有所帮助。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |