opcode是计算机指令中的一部分,用于指定要执行的操作, 指令的格式和规范由处理器的指令规范指定。 除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显式的操作数。 这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等。
通常opcode还有另一种称谓:字节码(byte codes)。 例如Java虚拟机(JVM),.NET的通用中间语言(CIL: Common Intermeditate Language)等等。
PHP中的opcode则属于前面介绍中的后着,PHP是构建在Zend虚拟机(Zend VM)之上的。PHP的opcode就是Zend虚拟机中的指令。
在PHP实现内部,opcode由如下的结构体表示:
2 |
opcode_handler_t handler; // 执行该opcode时调用的处理函数 |
8 |
zend_uchar opcode; // opcode代码 |
和CPU的指令类似,有一个标示指令的opcode字段,以及这个opcode所操作的操作数,PHP不像汇编那么底层, 在脚本实际执行的时候可能还需要其他更多的信息,extended_value字段就保存了这类信息, 其中的result域则是保存该指令执行完成后的结果。
例如如下代码是在编译器遇到print语句的时候进行编译的函数:
01 |
void zend_do_print(znode *result, const znode *arg TSRMLS_DC) |
03 |
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); |
05 |
opline->result.op_type = IS_TMP_VAR; |
06 |
opline->result.u.var = get_temporary_variable(CG(active_op_array)); |
07 |
opline->opcode = ZEND_PRINT; |
09 |
SET_UNUSED(opline->op2); |
10 |
*result = opline->result; |
这个函数新创建一条zend_op,将返回值的类型设置为临时变量(IS_TMP_VAR),并为临时变量申请空间, 随后指定opcode为ZEND_PRINT,并将传递进来的参数赋值给这条opcode的第一个操作数。这样在最终执行这条opcode的时候, Zend引擎能获取到足够的信息以便输出内容。
下面这个函数是在编译器遇到echo语句的时候进行编译的函数:
1 |
void zend_do_echo( const znode *arg TSRMLS_DC) |
3 |
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); |
5 |
opline->opcode = ZEND_ECHO; |
7 |
SET_UNUSED(opline->op2); |
可以看到echo处理除了指定opcode以外,还将echo的参数传递给op1,这里并没有设置opcode的result结果字段。 从这里我们也能看出print和echo的区别来,print有返回值,而echo没有,这里的没有和返回null是不同的, 如果尝试将echo的值赋值给某个变量或者传递给函数都会出现语法错误。
PHP脚本编译为opcode保存在op_array中,其内部存储的结构如下:
01 |
struct _zend_op_array { |
04 |
char *function_name; // 如果是用户定义的函数则,这里将保存函数的名字 |
05 |
zend_class_entry *scope; |
07 |
union _zend_function *prototype; |
09 |
zend_uint required_num_args; |
10 |
zend_arg_info *arg_info; |
11 |
zend_bool pass_rest_by_reference; |
12 |
unsigned char return_reference; |
13 |
/* END of common elements */ |
15 |
zend_bool done_pass_two; |
19 |
zend_op *opcodes; // opcode数组 |
23 |
zend_compiled_variable *vars; |
24 |
int last_var,size_var; |
如上面的注释,opcodes保存在这里,在执行的时候由下面的execute函数执行:
1 |
ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) |
3 |
// ... 循环执行op_array中的opcode或者执行其他op_array中的opcode |
前面提到每条opcode都有一个opcode_handler_t的函数指针字段,用于执行该opcode, 这里并没有给没有指定处理函数,那在执行的时候该由哪个函数来执行呢? 更多信息请参考后面的详细介绍。
PHP有三种方式来进行opcode的处理:CALL,SWITCH和GOTO,PHP默认使用CALL的方式,也就是函数调用的方式, 由于opcode执行是每个PHP程序频繁需要进行的操作,可以使用SWITCH或者GOTO的方式来分发, 通常GOTO的效率相对会高一些,不过效率是否提高依赖于不同的CPU。
延伸阅读
此文章所在专题列表如下:
- PHP内核探索:从SAPI接口开始
- PHP内核探索:一次请求的开始与结束
- PHP内核探索:一次请求生命周期
- PHP内核探索:单进程SAPI生命周期
- PHP内核探索:多进程/线程的SAPI生命周期
- PHP内核探索:Zend引擎
- PHP内核探索:再次探讨SAPI
- PHP内核探索:Apache模块介绍
- PHP内核探索:通过mod_php5支持PHP
- PHP内核探索:Apache运行与钩子函数
- PHP内核探索:嵌入式PHP
- PHP内核探索:PHP的FastCGI
- PHP内核探索:如何执行PHP脚本
- PHP内核探索:PHP脚本的执行细节
- PHP内核探索:操作码OpCode
- PHP内核探索:PHP里的opcode
- PHP内核探索:解释器的执行过程
- PHP内核探索:变量概述
- PHP内核探索:变量存储与类型
- PHP内核探索:PHP中的哈希表
- PHP内核探索:理解Zend里的哈希表
- PHP内核探索:PHP哈希算法设计
- PHP内核探索:翻译一篇HashTables文章
- PHP内核探索:哈希碰撞攻击是什么?
- PHP内核探索:常量的实现
- PHP内核探索:变量的存储
- PHP内核探索:变量的类型
- PHP内核探索:变量的值操作
- PHP内核探索:变量的创建
- PHP内核探索:预定义变量
- PHP内核探索:变量的检索
- PHP内核探索:变量的类型转换
- PHP内核探索:弱类型变量的实现
- PHP内核探索:静态变量的实现
- PHP内核探索:变量类型提示
- PHP内核探索:变量的生命周期
- PHP内核探索:变量赋值与销毁
- PHP内核探索:变量作用域
- PHP内核探索:诡异的变量名
- PHP内核探索:变量的value和type存储
- PHP内核探索:全局变量Global
- PHP内核探索:变量类型的转换
- PHP内核探索:内存管理开篇
- PHP内核探索:Zend内存管理器
- PHP内核探索:PHP的内存管理
- PHP内核探索:内存的申请与销毁
- PHP内核探索:引用计数与写时复制
- PHP内核探索:PHP5.3的垃圾回收机制
- PHP内核探索:内存管理中的cache
- PHP内核探索:写时复制COW机制
- PHP内核探索:数组与链表
- PHP内核探索:使用哈希表API
- PHP内核探索:数组操作
- PHP内核探索:数组源码分析
- PHP内核探索:函数的分类
- PHP内核探索:函数的内部结构
- PHP内核探索:函数结构转换
- PHP内核探索:定义函数的过程
- PHP内核探索:函数的参数
- PHP内核探索:zend_parse_parameters函数
- PHP内核探索:函数返回值
- PHP内核探索:形参return value
- PHP内核探索:函数调用与执行
- PHP内核探索:引用与函数执行
- PHP内核探索:匿名函数及闭包
- PHP内核探索:面向对象开篇
- PHP内核探索:类的结构和实现
- PHP内核探索:类的成员变量
- PHP内核探索:类的成员方法
- PHP内核探索:类的原型zend_class_entry
- PHP内核探索:类的定义
- PHP内核探索:访问控制
- PHP内核探索:继承,多态与抽象类
- PHP内核探索:魔术函数与延迟绑定
- PHP内核探索:保留类与特殊类
- PHP内核探索:对象
- PHP内核探索:创建对象实例
- PHP内核探索:对象属性读写
- PHP内核探索:命名空间
- PHP内核探索:定义接口
- PHP内核探索:继承与实现接口
- PHP内核探索:资源resource类型
- PHP内核探索:Zend虚拟机
- PHP内核探索:虚拟机的词法解析
- PHP内核探索:虚拟机的语法分析
- PHP内核探索:中间代码opcode的执行
- PHP内核探索:代码的加密与解密
- PHP内核探索:zend_execute的具体执行过程
- PHP内核探索:变量的引用与计数规则
- PHP内核探索:新垃圾回收机制说明
- php内核分析(六)-opcode
这里阅读的php版本为PHP-7.1.0 RC3,阅读代码的平台为linux 查看opcode php是先把源码解析成opcode,然后再把opcode传递给zend_vm进行执行的. // 一个op ...
- [Erlang 0125] Know a little Erlang opcode
Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的 ...
- String源码中的"avoid getfield opcode"
引言: 之前一篇文章梳理了String的不变性原则,还提到了一段源码中注释"avoid getfield opcode",当时通过查阅资料发现,这是为了防止 getfield(获取 ...
- 通过VLD扩展分析PHP opcode
安装VLD扩展 ./configure --with-php-config=/usr/local/php/bin/php-config --enable-vld 生成脚本opcode > p ...
- opcode的执行
原文链接:http://www.orlion.ga/1001/ 当.php文件被编译为opcode后,下一步的执行并非是把opcode编译为机器码而是类似于如下的方式执行: while (TRUE) ...
- 【转】中间代码opcode的执行
原文链接:http://www.orlion.ga/941/ 原文:http://www.nowamagic.net/librarys/veda/detail/1543 假如我们现在使用的是CLI模式 ...
- PHP扩展编写、PHP扩展调试、VLD源码分析、基于嵌入式Embed SAPI实现opcode查看
catalogue . 编译PHP源码 . 扩展结构.优缺点 . 使用PHP原生扩展框架wizard ext_skel编写扩展 . 编译安装VLD . Debug调试VLD . VLD源码分析 . 嵌 ...
- PHP Opcode内核实现 - [ PHP内核学习 ]
catalogue . Opcode简介 . PHP中的Opcode . opcode翻译执行(即时解释执行) 1. Opcode简介 opcode是计算机指令中的一部分,用于指定要执行的操作, 指令 ...
- 深入了解php opcode缓存原理
什么是opcode opcode(operate code)是计算机指令中的一部分,用于指定要执行的操作,指令的格式和规范由处理器的指定规范指定 opcode是一种php脚本编译后的中间语言,就像ja ...
- 黄聪:深入理解PHP Opcode缓存原理
什么是opcode缓存? 当解释器完成对脚本代码的分析后,便将它们生成可以直接运行的中间代码,也称为操作码(Operate Code,opcode).Opcode cache的目地是避免重复编译,减少 ...
随机推荐
- C++ 资源管理 —— RAII
RAII:在构造函数中申请资源,在析构函数中释放资源. 1. RAII 自动实现锁资源的释放 void bad() { m.lock(); f(); if (COND) return; m.unloc ...
- 【JQuery】学习
JavaScript参考 JQuery 学习总结及实例 1.JQuery概念 A.Jquery是一个优秀的Javascript框架.它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器,jQuery ...
- mysql 执行批量的sql
<?xml version="1.0" encoding="utf-8" ?> <dbconfig> <!-- 数据库驱动 --& ...
- 解决VS2008 调试启动特别慢
Resolving Very Slow Symbol Loading with VS 2008 during debugging Recently, I was encountering insane ...
- SQL2005 如何在没有日志文件的情况下如何恢复MDF数据库文件?
第一步:先建立一个同名数据库,停止SQL SERVER2005,将没有日志的的.mdf数据库文件覆盖刚新建的.mdf数据库文件,重新启动数据库. 第二步:在查询分析器中运行如下代码(将数据库名修改为您 ...
- jQuery选择器this通过onclick传入方法以及Jquery中的this与$(this)初探,this传处变量等
起初以为this和$(this)就是一模子刻出来.但是我在阅读时,和coding时发现,总不是一回事. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...
- java ------------用Array.sort()输出数组元素的最大值,最小值,平均值~~~~
总结:输出最大值,最小值,可以先排序,再输出就容易点了.不用循环判断.我还没学.但是觉得很好用 package com.aini; import java.util.Arrays; import ja ...
- 水仙花之java与c++的战争======
总结:同样在C++里可以运行正常的水仙花,在java里不行 为什么??是运算符优先级的问题吗: package com.a; //水仙花数 一个三位数 324:426/195 public class ...
- java二维数组求每行最大值,每列最小值,及输出数组主对角线上的元素
总结:完全搞不懂,行和列是怎么弄的,,,,, package com.c2; import java.util.Scanner; public class Oaa { public static vo ...
- 网络编程Socket之wireshark使用
这里只对wireshark进行简单的使用介绍.里面的报表,IO图形分析等以后再谈.... 这里不提供下载地址了,不是什么稀有资源,我使用的wireshark是在百度软件中心下载的. 第一步:选择需要 ...