PHP内核 - 编译与执行 - 语言的编译与执行

PHP   PHP内核  

一、前言

  PHP 内部最核心的两个阶段就是编译、执行,这也是 ZendVM 中最重要、最复杂的部分。

  PHP 的编译与执行是两个相对独立的阶段,其中编译的流程涉及词法分析、语法分析、抽象语法树的生成;而执行阶段则是根据编译阶段输出的产物(也就是opline指令)进行执行。

  语言的本质是一种信息交流的规则,通过约定俗成的规则让万物有了沟通的能力,语言并没有固定的法则,正因为如此,才在人类的世界里产生了如此之多的语言。

  尽管不同语言之间差异很大,但语言之间总能建立某种特定的关系,这种特定的关系可以将某种语言表达的信息转为另一种语言可感知的信息,我们把这个过程称为翻译。

  编译语言是人类与计算机之间的交流规则,与人类的自然语言一样,也需要一个翻译的过程,因为计算机只认识自己的机器语言,无法理解人类定义的高级编程语言,这个翻译过程称之为编译。

  根据编译时机的不同,编程语言可以分为编译型、解释型。

  (1)编译型语言是指在程序运行前,提前编译为计算机可执行的二进制文件,在执行时直接执行机器指令,这种类型的典型代表就是 C、C++、Golang;

  (2)解释型语言是指程序在运行时由编译器边编译边执行,也称作脚本语言,PHP 正是属于这种类型。

  

二、编译型语言

  编译型语言由编译器负责语言的“翻译”工作,这个步骤是在线下完成的,在执行前编译器根据不同的机器类型将高级语言生成对应的低级机器语言指令,然后将这些机器语言指令按照可执行目标程序的格式存储到磁盘文件中,执行时再按照可执行程序的格式将其加载到内存中的对应位置(数据段、代码段),最后逐条执行机器指令。

  编译型语言执行的是机器可直接识别的指令,它最大的优势就是效率高,执行时不需要经过耗时的编译过程。编译型语言在不同平台上需要进行重新编译,因为编译时生成的机器指令是针对特定机器的,比如 Windows下的编译生成的可执行程序在 Linux 系统中是无法执行的。

  以C语言为例, Linux下由 GCC 编译器读取程序的源文件,经过预处理、编译、汇编、链接四个过程,将定义的 C 语言代码编译为可执行的目标程序。如图:

  

  
(1)预处理

预处理环节主要对C语言程序中以#开头的命令进行替换:替换 include包含的文件,用实际值替换 define定义的字符串,根据 #if 条件决定要编译的代码。通过gcc -E xxx.c 命令完成预处理,处理完成后的结果仍然是 C 语言代码。

(2)编译

编译过程是将经过预处理后的 C 语言代码转换为汇编语言,通过以下命令完成 gcc -s
xxx.i 。转换后的结果就是汇编代码,仍然是可理解的文本文件。

(3)汇编

汇编器将汇编代码生成机器指令,并生成扩展名为 .o 的ELF可重定位目标文件。可重定位目标文件包括二进制机器指令及数据,由各个数据节(section))组成,包含数据节、代码节、符号表等。

(4)链接

链接是生成可执行程序的最后一步,链接器将可重定位目标文件中引用其他文件的符号进
行替换,同时,根据上一步骤生成的符号表,把函数、全局变量的引用位置替换为实际的存储位置。(比如生成函数调用的指令,需要知道函数代码段的起始位置,这个信息就从符号表中获取。)

  

三、解释型语言

  解释型语言与编译型语言的根本区别在于,解释型语言在执行前不需要编译为机器语言,而是由解析器进行解析、执行。

  解释器为机器可识别的二进制程序。也就是说,解释型语言实际上是在语言与实际计算机之间加了一层解释器,也称为虚拟机,然后通过解释型语言控制编译好的解释器执行相应的指令,如图:

  解释型语言与解释器之间的“翻译”并不需要机器语言,而是由解析器自己确定的一种规则,这种规则可以让解释器知道该执行什么样的机器指令。这里一定要明确,解释器并不是将解释型语言编译为机器语言再去执行的,而是解释器本身预先定义好了一些具体的操作,这些操作被编译为机器指令,解释型语言在执行时,告诉解释器该执行哪段机器指令。

  解释型语言与实际计算机之间多了一层解释器,屏蔽了不同平台之间机器语言的差异,因此解释型语言可以方便的运行在不同平台对应的解释器上,由解释器处理不同平台之间的差异,实现跨平台运行。

  由此换来的代价就是运行效率低,与编译型语言直接执行机器语言相比,解释型语言多了解释器解析这一步。另外,同样的计算,解释型语言往往需要执行更多的指令才能完成,比如加法操作,机器指令只需要一条,而在解释器中可能就需要调用一个函数才能完成。

 

 



Top