• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • Zend 引擎

    Zend Engine 是 PHP 实现运行的核心,提供了语言实现上的基础设施。例如:PHP的语法实现,脚本的编译运行环境,扩展机制以及内存管理等,当然这里的 PHP 指的是官方的 PHP 实现(除了官方的实现,目前比较知名的有 facebook 的 hiphop 实现,不过到目前为止,PHP 还没有一个标准的语言规范),而 PHP 则提供了请求处理和其他 Web 服务器的接口(SAPI)。

    Zend Engine 最主要的特性就是把 PHP 的边解释边执行的运行方式改为先进行预编译(Compile),然后再执行(Execute)。这两者的分开,降低了模块间耦合度,可扩展性也大大增强。

    PHP 经常会被定义为“脚本语言”或者是“解释型语言”。所谓“解释型语言”就是指用这种语言写的程序不会被直接编译为本地机器语言(native machine language),而是会被编译为一种中间形式(代码),很显然这种中间形式不可能直接在CPU上执行(因为CPU只能执行本地机器指令),但是这种中间形式可以在使用本地机器指令(如今大多是使用 C 语言)编写的软件上执行。

    这个软件就是所谓的软件虚拟机(software virtual machine)。我们先看下Wikipedia上对软件虚拟机的定义:进程虚拟机(process virtual machine)通过提供一个抽象的平台独立的程序执行环境来执行某种计算机程序。进程 VM(virtual machine)有时候也被称为应用程序虚拟机,或者是可管理运行环境(Manged Runtime Environment,简称 MRE),它会以一个普通应用的形式运行在宿主操作系统中(host OS),在运行中,他是宿主操作系统中一个单独的进程。在这个进程启动时,VM 会被创建,退出时则会被销毁。它的目的是提供一种平台独立的程序执行环境,它可以对底层硬件或操作系统进行抽象处理,从而保证应用程序可以在任何平台上以一致的行为执行。

    跟任何解释型语言一样,PHP 语言也被设计为一种可以跨平台执行抽象指令的程序,尽可能地抽离掉底层操作系统的细节。这是技术上的解释。在功能应用上,PHP 的主要领域是 Web 应用(PHP的目的是解决Web应用的相关问题)。

    当前市面上还有其他一些基于软件虚拟机的语言(不完全列表):Java、Python、C#、Ruby、Pascal、Lua、Perl、Javascript等等。基本上使用这些语言编写的程序都不会被直接编译为本地机器指令,这些程序都是运行在一个软件虚拟机上。为了性能考虑,有些软件虚拟机可以把部分(并非全部)语言的中间代码直接转换为机器指令来执行:这个过程被称为“JIT编译”。

    软件虚拟机之所以如此流行,主要是因为我们不想为了在屏幕上输出一个“Hello”而写几千行的C代码(译注:这个有点夸张了吧,不过考虑到平台相关性,使用C写程序确实要考虑更多,至少是需要在不同的机器上编译所写的程序,但为了输出一个“hello”写几千行的代码就太夸张了,因为输出“hello”的主要工作都是由 C 运行库提供的函数完成的,尽管不同平台运行库的代码肯定有差别,但接口还是基本上完全一致的)。使用软件虚拟机相对于基于本地平台的程序开发有如下优点:

    • 使用简单,便于开发。
    • 基本上都支持自动内存管理。
    • 支持抽象目标数据类型,没必要进行底层的数学运算(译注:这个应该指的是地址运算,这个在 C 中和汇编编程中都非常常见),没必要因为切换目标硬件平台而重新编写代码。

    当然也有些不足之处:

    • 不可能精确地管理内存或全局资源的使用(唯有信任 VM)。
    • 执行速度无法比拟本地机器代码:完成相同的任务需要更多的CPU周期(JIT就是用于减小这个差距的,但不可能消除)。
    • 可能会抽象很多东西,通常程序员会远离硬件,从而无法全面理解代码的确切影响,特别是在负载过大的情况下。

    随着时间的推移,我越来越注意到一个事实:越来越少的程序员能够确切地掌握他们所写的代码对硬件和网络的影响,在我个人看来这并非是什么好现象。这就像某个人把两根电线连在一起,然后双手合十祈祷整个系统不要挂掉。当然我的意思并不是希望大家能够掌握整个链条上的所有东西,这不是人力可为的,我只是希望大家至少清楚我们所谈论的这些东西的深层含义。

    所以我会尽力向你展示 PHP 对你所写的代码做了什么。当你掌握了这些知识后,你可以从举一反三,把这些知识应用到任何其他“解释型”语言上,因为它们在设计上跟 PHP 的差别不大,它们有很多共通的概念。通常而言,你在学习其他的解释型语言的过程中,你会发现它们之间的主要区别只是是否使用了JIT,或者是否可以并行执行(主要是使用线程,PHP没有提供任何并行技术),其他的差别可能就是内存池(memory pooling)和垃圾回收算法上的不同了。


    Zend 软件虚拟机

    PHP 使用主要虚拟机(Zend 虚拟机,译注:HHVM 也是一种执行 PHP 代码的虚拟机,但很显然 Zend 虚拟机还是目前的主流)可以分为两大部分,它们是紧密相连的:

    • 编译栈(compile stack):识别PHP语言指令,把它们转换为中间形式。
    • 执行栈(execution stack):获取中间形式的代码指令并在引擎上执行,引擎是用C或者汇编编写成的。

    这篇文章不会谈论第一部分,而会专注于 Zend 虚拟机的 executor(译注:executor可以翻译为执行器,但似乎鲜有这种说法,所以后面直接使用这个英文单词,而不作翻译),executor 是一个很意思的软件,它具有高度优化、跨平台、运行时 hookable(译注:这个词不好翻译,大概意思就是可以通过设置某种钩子获取运行时信息,像VLD这种扩展就是这么实现的)等优点,在技术上非常有挑战性。它包含几千行 C 代码,每次新发布的 PH P版本都会对它做部分重写。


    OPCode

    OPCode 也会出现在所谓的字节码(byte codes)中,或者是被软件解释器(而不是硬件设备)解释执行的指令的其他形式中。这些软件指令集通常会提供一些比对应的硬件指令集更高级(higher-level)的数据类型和操作,尽管它们每个指令执行的结果都是差不多的。

    ByteCode 和 OPCode 其实是两个含义不同的词,但我们经常会把它们当作同一个意思来交互使用。

    我们假设 Zend VM 的一个 OPCode 对应虚拟机的一个底层操作。Zend 虚拟机有很多 OPCode:它们可以做很多事情。随着 PHP 的发展,也引入了越来越多的 OPCode,这都是源于 PHP 可以做越来越多的事情。你可以在 PHP 的源代码文件 Zend/zend_vm_opcodes.h 中看到所有的 OPCode。

    通常而言,OPCode 的名称是自描述的,例如:

    • ZEND_ADD :执行两个操作数的算术加法运算。
    • ZEND_NEW :创建一个对象(一个PHP对象)。
    • ZEND_EXIT :退出PHP执行。
    • ZEND_FETCH_DIM_W :取一个操作数在某个维度(dimension)下的值,然后执行写入操作(译注:这里的“维度”指的一维数组,二维数组的“维度”,给数组中的某个元素赋值,或者是给字符串所在某个位置的字符赋值都会用到这个 OPCode)。