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的目地是避免重复编译,减少 ...
随机推荐
- Ubuntu安装最新版的nodejs
安装玩Ubuntu的虚拟机之后安装nodejs发现npm的版本才3.5.2,这都多老了?于是Google了一下,发现是由于Ubuntu官方维护的包源太老了,想要安装nodejs的最新版,两种方法,一种 ...
- 使用WPScan破解wordpress站点密码
我这里使用的Kali Linux,它默认安装了WPScan. 在使用WPScan之前,先更新它的漏洞数据库: # wpscan –update 扫描wordpress用户 wpscan -–url [ ...
- docker 基本学习
Docker的应用场景: 加速本地开发和构建流程,使其更加高效.更加轻量化.本地开发人员可以构建.运行并分享Docker容器.容器可以在开发环境中构建,然后轻松地提交到测试环境中,并最终进入生产环境. ...
- notebook查找文件
- 数据结构之最小生成树Kruskal算法
1. 克鲁斯卡算法介绍 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法. 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路. 具体做法:首先构造一个 ...
- bzoj 4465 游戏中的学问
Written with StackEdit. Description 大家应该都见过很多人手拉手围着篝火跳舞的场景吧?一般情况下,大家手 拉手跳舞总是会围成一个大圈,每个人的左手拉着旁边朋友的右手, ...
- iOS6 自动布局 入门–Auto Layout
目前为止,即使你的界面设计是在合理的复杂度内,你也必须要为之写许多代码来适应变化的布局.现在我相信你会很高兴听到这种情况将不会发生了-对于iPhone与iPad IOS6 带来了一个非常了不起的特征: ...
- LA3890 Most Distant Point from the Sea
题意 PDF 分析 可以二分答案,检验就用半平面交,如果平面非空则合法. 时间复杂度\(O(T n \log^2 n)\) 代码 #include<iostream> #include&l ...
- oracle系统表的查询
oracle查询用户下的所有表 select * from all_tab_comments -- 查询所有用户的表,视图等select * from user_tab_comments -- 查 ...
- 洛谷 4721 【模板】分治 FFT——分治FFT / 多项式求逆
题目:https://www.luogu.org/problemnew/show/P4721 分治FFT:https://www.cnblogs.com/bztMinamoto/p/9749557.h ...