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的目地是避免重复编译,减少 ...
随机推荐
- git配置ssh key并从github.com拉取repos
一.配置ssh key 1. 进入当前用户目录cd ~2. 生成ssh keyssh-keygen -t rsa -C "ABC@qq.com"ABC@qq.com账号必须是你登录 ...
- Leetcode 944. Delete Columns to Make Sorted
class Solution: def minDeletionSize(self, A: List[str]) -> int: ans = 0 for j in range(len(A[0])) ...
- Ubuntu安装最新版的nodejs
安装玩Ubuntu的虚拟机之后安装nodejs发现npm的版本才3.5.2,这都多老了?于是Google了一下,发现是由于Ubuntu官方维护的包源太老了,想要安装nodejs的最新版,两种方法,一种 ...
- HihoCoder 1185 : 连通性·三(强连通缩点)
连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出去,就拜托小Hi ...
- pmm监控页面502
我们知道pmm主要使用的是 普罗米修斯采集和grafana日志统计显示. 最近为硬盘扩过一次容量,主要是docker使用的,我的pmm是跑在docker上的,但是重启后pmm的debug日志下载502 ...
- jenkins部署应用
1. 系统介绍 Jenkins系统提供了一键部署的作用,整个过程有从提测的分支抓取代码,编译,打包,把打的包部署在应用服务器上,基本有Service,Web和Worker等. 2. Jen ...
- C# 程序部署、调试时间长的解决办法
最近在做数控折弯机项目时,VS2008环境下采用C#..NET Compact Framework开发WinCE.Windows Mobile程序时,编译项目非常慢,看着进度条慢慢刷,有时候需要几分钟 ...
- 【openCV学习笔记】【2】读取并播放一段视频
#include <iostream> #include <opencv/highgui.h> int main(int argc, char** argv){ cvNamed ...
- AppCan上下拉列表刷新
function initBounce(funcTop, funcBottom){ uexWindow.setBounce("1"); if (!funcTop && ...
- java代码求输入数的平均值~~~~
总结:1.谢谢程老师,一个很好的老师,人很普通,但是浑浊的世界里,那一份真实感动到底~~~~很感谢他 2.这里注意两个方面,也是我最大的弱点:循环和数组的length属性.前者运用不灵活,后者自己总是 ...