PHP生命周期的学习笔记(er)
上回主要记录了SAPI相关的知识。
这次简单来记录一下ZE方面的知识。
深入理解Zend执行引擎
原文链接
PHP是解释型语言,所谓“解释型语言”就是指用这种语言写的程序不会被直接编译为本地机器语言(native machine language),而是会被编译为一种中间形式(代码),很显然这种中间形式不可能直接在CPU上执行(因为CPU只能执行本地机器指令),但是这种中间形式可以在使用本地机器指令(如今大多是使用C语言)编写的软件上执行。
这个软件就是所谓的软件虚拟机(software virtual machine)我们先看下Wikipedia上对软件虚拟机的定义:
(…)进程虚拟机(process virtual machine)通过提供一个抽象的平台独立的程序执行环境来执行某种计算机程序。进程VM(译注:VM是virtual machine的缩写)有时候也被称为应用程序虚拟机,或者是可管理运行环境(Manged Runtime Environment,简称MRE),它会以一个普通应用的形式运行在宿主操作系统中(host OS),在运行中,他是宿主操作系统中一个单独的进程。在这个进程启动时,VM会被创建,退出时则会被销毁。它的目的是提供一种平台独立的程序执行环境,它可以对底层硬件或操作系统进行抽象处理,从而保证应用程序可以在任何平台上以一致的行为执行。
跟任何解释型语言一样,PHP语言也被设计为一种可以跨平台执行抽象指令的程序,尽可能地抽离掉底层操作系统的细节。这是技术上的解释。在功能应用上,PHP的主要领域是Web应用(PHP的目的是解决Web应用的相关问题)。
当前市面上还有其他一些基于软件虚拟机的语言(不完全列表):Java、Python、C#、Ruby、Pascal、Lua、Perl、Javascript等等。基本上使用这些语言编写的程序都不会被直接编译为本地机器指令,这些程序都是运行在一个软件虚拟机上。为了性能考虑,有些软件虚拟机可以把部分(并非全部)语言的中间代码直接转换为机器指令来执行:这个过程被称为“JIT编译”。在我写这篇文章时PHP并未使用JIT编译技术,不过有些实验性的工作正在进行,PHP社区也经常会提及和探讨这个话题。(译注:这篇文章写于15年2月份,此时HHVM应该已经发布,而HHVM就使用了JIT,另外最新版的PHP7貌似也使用了JIT)
Q: 这里我们产生一个疑问,既然解释型语言也要进行编译,为什么不直接编译成机器码,机器码不是快么?
编译型语言和解释型语言的优缺点
原文链接
我们只挑主要的说。原文中列举了更多的不同。
编译型语言的优点就是执行快,缺点就是它的平台依赖性,我们需要根据不同的平台对我们的代码来进行编译。
而上面的缺点正是解释型语言的优点,解释型语言只需要编译一次,生成中间代码,然后在不同的平台上,我们只需要安装对应平台的虚拟机就可以运行我们的程序了,而不用反复为不同平台进行编译打包。缺点就是运行速度慢一点。
另外解释型的优点还有包体较小,一般编译型语言的包体都会很大。
并且解释型语言一般方便调试,编译型语言不方便调试。最好的对比就是空指针问题。
言归正传
PHP虚拟机一般分为两部分结构
PHP虚拟机一般分为两部分结构(compiler & executor)
- 编译栈(compile stack):识别PHP语言指令,把它们转换为中间形式
- 执行栈(execution stack):获取中间形式的代码指令并在引擎上执行,引擎是用C或者汇编编写成的
OPCode
ZE会将PHP编译成中间代码--OPCode (Operation code),那中间代码长啥样呢?详细的建议去阅读这篇文章。这里简单的记录一下,OPCode里面对应我们的硬件指令集进行了一些相应的指令实现,是一个软件指令集
通常而言,OPCode的名称是自描述的,例如:
ZEND_ADD :执行两个操作数的算术加法运算
ZEND_NEW :创建一个对象(一个PHP对象)
ZEND_EXIT :退出PHP执行
ZEND_FETCH_DIM_W : 取一个操作数在某个维度(dimension)下的值,然后执行写入操作(译注:这里的“维度”指的一维数组,二维数组的“维度”,给数组中的某个元素赋值,或者是给字符串所在某个位置的字符赋值都会用到这个OPCode)
等等
PHP5.6有167个OPCode。因此我们可以说PHP5.6的虚拟机的executor可以执行167种不同的(计算)操作。
OPCache
原文链接
OPCache 是ZD的一个插件,他提供了对OPCode一个缓存机制。
OPCache是一种通过将解析的PHP脚本预编译的字节码(Operate Code)存放在共享内存中来避免每次加载和解析PHP脚本的开销,解析器可以直接从共享内存读取已经缓存的字节码(Operate Code),从而大大提高PHP的执行效率
OPCode Cache的目地是避免重复编译,减少CPU和内存开销。如果动态内容的性能瓶颈不在于CPU和内存,而在于I/O操作,比如数据库查询带来的磁盘I/O开销,那么OPCode Cache的性能提升是非常有限的。但是既然OPCode Cache能带来CPU和内存开销的降低,这总归是好事。现代操作码缓存器(Optimizer+,APC2.0+,其他)使用共享内存进行存储,并且可以直接从中执行文件,而不用在执行前“反序列化”代码。这将带来显着的性能加速,通常特别是高流量和高并发量时降低了整体服务器的内存消耗,而且很少有缺点。
为什么要使用OPCode缓存?
这得从PHP代码的生命周期说起,请求PHP脚本时,会经过五个步骤,如下图所示:
1542960146821314.png
Zend引擎必须从文件系统读取文件、扫描其词典和表达式、解析文件、创建要执行的计算机代码(称为OPCode),最后执行OPCode。每一次请求PHP脚本都会执行一遍以上步骤,如果PHP源代码没有变化,那么OPCode也不会变化,显然没有必要每次都重行生成OPCode,结合在Web中无所不在的缓存机制,我们可以把OPCode缓存下来,以后直接访问缓存的OPCode岂不是更快,启用OPCode缓存之后的流程图如下所示:
1542960158864729.png
因此使用了Operate Code缓存之后,PHP代码会直接获取OPCode后直接执行,中间的三个步骤会省略掉因此会大幅提高PHP代码执行效率
参考:
https://blog.csdn.net/fff058/article/details/78692261
https://blog.csdn.net/girldwh0715/article/details/70228553
https://m.php.cn/article/413238.html