经过前面的学习,咱们了解到一个PHP文件在服务器端的执行过程包括如下两个大的过程: php
在第二步中,词法分析、语法分析,编译中间代码,执行中间代码等各个部分统称为Zend虚拟机。 与Java、C#等编译型语言相比,PHP少了一个手动编译的过程,它们无需编译便可运行,咱们称其为解释性语言。 Java有本身的Java虚拟机,它在多个平台上实现统一语言; C#有本身的.NET虚拟机,它在单一平台实现多种语言; PHP跟他们同样,也有属于本身的Zend虚拟机。它们在本质是相同的,它们都是抽象的计算机。 这些虚拟机都是在某种较底层的语言上抽象出另一种语言,有本身的指令集,有本身的内存管理体系。 它们最终都会将抽象级别较高的语言实现转化为抽象级别较低的语言实现, 而且实现其它辅助功能,如内存管理,垃圾回收等机制, 以减小程序员在具体实现上的工做,从而能够将更多的时间和精力投入到业务逻辑中。 从抽象层次看,Zend虚拟机比Java等语言更高级一些,这里的高级不是说功能更强大或效率更高, 简单点说,Zend虚拟机离真正的机器实现更远一些。 最近这些年,语言的发展只是不断的抽象,不断的远离机器,没有根本性的变化。 程序员
这里咱们从虚拟机的前世此生讲起,叙述Zend虚拟机的实现原理,关键的数据结构, 并其中穿插一个关于语法实现的示例和源码加密解密的过程说明。 算法
在wiki中虚拟机的定义是: 虚拟机(Virtual Machine),在计算机科学中的体系结构里,是指一种特殊的软件, 他能够在计算机平台和终端用户之间建立一种环境,而终端用户则是基于这个软件所建立的环境来操做软件。 在计算机科学中,虚拟机是指能够像真实机器同样运行程序的计算机的软件实现。 数组
虚拟机是一种抽象的计算机,它有本身的指令集,有本身的内存管理体系。 在此类虚拟机上实现的语言比较低抽象层次的语言更加明了,更加简单易学。 缓存
PHP文件是如何被解析的,生成的中间代码表示什么,生成的中间代码与实际的PHP代码是如何对应的,生成的中间代码如何被执行的? 在执行过程当中会将会哪些中间的数据?整个虚拟机是否能够优化?如何优化? 服务器
从概念层将Zend虚拟机的实现进行抽象,咱们能够将Zend虚拟机的体系结构分为:解释层、执行引擎、中间数据层。 数据结构
Zend虚拟机体系结构图 闭包
当一段PHP代码进入Zend虚拟机,它会被执行两步操做:编译和执行。 对于一个解释性语言来讲,这是一个创造性的举动,可是,如今的实现并不完全。 如今当PHP代码进入Zend虚拟机后,它虽然会被执行这两步操做,可是这两步操做对于一个常规的执行过程来讲倒是连续的, 也就是说它并无转变成和Java这种编译型语言同样:生成一个中间文件存放编译后的结果。 若是每次执行这样的操做,对于PHP脚本的性能来讲是一个极大的损失。 虽然有相似于APC,eAccelerator等缓存解决方案。可是其本质上是没有变化的,而且不能将两个步骤分离,各自发展壮大。 函数
解释层是Zend虚拟机执行编译过程的位置。它包括词法解析、语法解析和编译生成中间代码三个部分。 词法分析就是将咱们要执行的PHP源文件,去掉空格,去掉注释,切分为一个个的标记(token), 而且处理程序的层级结构(hierarchical structure)。 源码分析
语法分析就是将接受的标记(token)序列,根据定义的语法规则,来执行一些动做,Zend虚拟机如今使用的Bison使用巴科斯范式(BNF) 来描述语法。 编译生成中间代码是根据语法解析的结果对照Zend虚拟机制定的opcode生成中间代码, 在PHP5.3.1中,Zend虚拟机支持135条指令(见Zend/zend_vm_opcodes.h文件), 不管是简单的输出语句仍是程序复杂的递归调用,Zend虚拟机最终都会将全部咱们编写的PHP代码转化成这135条指令的序列, 以后在执行引擎中按顺序执行。
当Zend虚拟机执行一个PHP代码时,它须要内存来存储许多东西, 好比,中间代码,PHP自带的函数列表,用户定义的函数列表,PHP自带的类,用户自定义的类, 常量,程序建立的对象,传递给函数或方法的参数,返回值,局部变量以及一些运算的中间结果等。 咱们把这些全部的存放数据的地方称为中间数据层。
若是PHP以mod扩展的方式依附于Apache2服务器运行,中间数据层的部分数据可能会被多个线程共享,若是PHP自带的函数列表等。 若是只考虑单个进程的方式,当一个进程被建立时它就会被加载PHP自带的各类函数列表,类列表,常量列表等。 当解释层将PHP代码编译完成后,各类用户自定义的函数,类或常量会添加到以前的列表中, 只是这些函数在其自身的结构中某些字段的赋值是不同的。
当执行引擎执行生成的中间代码时,会在Zend虚拟机的栈中添加一个新的执行中间数据结构(zend_execute_data), 它包括当前执行过程的活动符号列表的快照、一些局部变量等。
Zend虚拟机的执行引擎是一个很是简单的实现,它只是依据中间代码序列(EX(opline)),一步一步调用对应的方法执行。 在执行引擎中没并有相似于PC寄存器同样的变量存放下一条指令,当Zend虚拟机执行到某条指令时,当它全部的任务都执行完了, 这条指令会本身调用下一条指令,即将序列的指针向前移动一个位置,从而执行下一条指令,而且在最后执行return语句,如此反复。 这在本质上是一个函数嵌套调用。
回到开头的问题,PHP经过词法分析、语法分析和中间代码生成三个步骤后,PHP文件就会被解析成PHP的中间代码opcode。 生成的中间代码与实际的PHP代码之间并无彻底的一一对应关系。只是针对用户所给的PHP代码和PHP的语法规则和一些内部约定生成中间代码, 而且这些中间代码还须要依靠一些全局变量中转数据和关联。至于生成的中间代码的执行过程是依据中间代码的顺利, 依赖于执行过程当中的全局变量,一步步执行。固然,在遇到一些函数跳转也会发生偏移,可是最终仍是会回到偏移点。
此文章所在专题列表以下: