从PHP,Java和C语言的编译执行过程能够先解释下编译型语言和解释型语言。php
编译型语言html
程序在执行以前须要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不须要从新翻译,直接使用编译的结果就好了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、Delphi等.java
解释型语言express
程序不须要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次。所以效率比较低。好比Basic语言,专门有一个解释器可以直接执行Basic程序,每一个语句都是执行的时候才翻译。(在运行程序的时候才翻译,专门有一个解释器去进行翻译,每一个语句都是执行的时候才翻译。效率比较低,依赖解释器,跨平台性好.)ubuntu
下面都是鸟哥博客的内容:深刻理解PHP原理之opcodeide
hello.php <?php echo "Hello World"; $a = 1 + 1; echo $a; ?>
Zend引擎对这个hello.php文件进行词法分析,语法分析,编译成opcode,而后执行opcode。这个Zend引擎是安装PHP时安装的。看看这个文件是如何运行的,会通过以下4个阶段:函数
php hello.php 1.Scanning(Lexing) ,将PHP代码转换为语言片断(Tokens) 2.Parsing, 将Tokens转换成简单而有意义的表达式 3.Compilation, 将表达式编译成Opocdes 4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。
在操做系统中执行php命令也就是运行Zend引擎,而后Zend引擎拿到hello.php文件
那什么是Lexing? 学过编译原理的同窗都应该对编译原理中的词法分析步骤有所了解,Lex就是一个词法分析的依据表。 Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l(Lex文件),来输入的 PHP代码进行词法分析,从而获得一个一个的“词”,PHP4.2开始提供了一个函数叫token_get_all,这个函数就能够讲一段PHP代码 Scanning成Tokens;
若是用这个函数处理咱们开头提到的PHP代码,将会获得以下结果:操作系统
Array ( [0] => Array ( [0] => 367 [1] => Array ( [0] => 316 [1] => echo ) [2] => Array ( [0] => 370 [1] => ) [3] => Array ( [0] => 315 [1] => "Hello World" ) [4] => ; [5] => Array ( [0] => 370 [1] => ) [6] => = [7] => Array ( [0] => 370 [1] => ) [8] => Array ( [0] => 305 [1] => 1 ) [9] => Array ( [0] => 370 [1] => ) [10] => + [11] => Array ( [0] => 370 [1] => ) [12] => Array ( [0] => 305 [1] => 1 ) [13] => ; [14] => Array ( [0] => 370 [1] => ) [15] => Array ( [0] => 316 [1] => echo ) [16] => Array ( [0] => 370 [1] => ) [17] => ; )
分析这个返回结果咱们能够发现,源码中的字符串,字符,空格,都会原样返回。每一个源代码中的字符,都会出如今相应的顺序处。而,其余的好比标签,操做符,语句,都会被转换成一个包含俩部分的Array: Token ID (也就是在Zend内部的改Token的对应码,好比,T_ECHO,T_STRING),和源码中的原来的内容。
接下来,就是Parsing阶段了,Parsing首先会丢弃Tokens Array中的多余的空格,而后将剩余的Tokens转换成一个一个的简单的表达式.net
> 1.echo a constant string > 2.add two numbers together > 3.store the result of the prior expression to a variable > 4.echo a variable
1.Opcode数字的标识,指明了每一个op_array的操做类型,好比add , echo
2.结果 存放Opcode结果
3.操做数1 给Opcode的操做数
4.操做数2
5.扩展值 1个整形用来区别被重载的操做符翻译
而后就改Compilation阶段了,它会把Tokens编译成一个个op_array, 每一个op_array包含以下5个部分
其中opcode数字标识符对应zend_vm_opcode.h中的指令
参考laruence:opcode列表
好比,咱们的PHP代码会被Parsing成:
* ZEND_ECHO 'Hello World' * ZEND_ADD ~0 1 1 * ZEND_ASSIGN !0 ~0 * ZEND_ECHO !0
JVM执行程序的过程 :
I.加载.class文件
II.管理并分配内存
III.执行垃圾收集
JRE(java运行时环境)包含JVM的java程序的运行环境 [1]
JVM是Java程序运行的容器,可是他同时也是操做系统的一个进程,所以他也有他本身的运行的生命周期,也有本身的代码和数据空间。
JVM在整个jdk中处于最底层,负责与操做系统的交互,用来屏蔽操做系统环境,提供一个完整的Java运行环境,所以也就虚拟计算机.操做系统装入JVM是经过jdk中Java.exe来完成,经过下面4步来完成JVM环境。
1.建立JVM装载环境和配置
2.装载JVM.dll
3.初始化JVM.dll并挂接到JNIENV(JNI调用接口)实例
4.调用JNIEnv实例装载并处理class类。
参考原文:C语言编译过程详解
平时开发中,你们可能一行代码就编译好了源代码,以下:
$ gcc hello.c # 编译 $ ./a.out # 执行 hello world!
这个过程如此熟悉,以致于你们以为编译事件很简单的事。事实真的如此吗?咱们来细看一下C语言的编译过程究竟是怎样的。
上述gcc命令其实依次执行了四步操做:
1.预处理(Preprocessing)
2.编译(Compilation)
3.汇编(Assemble)
4.连接(Linking)
示例代码: // test.c #include <stdio.h> #include "mymath.h"// 自定义头文件 int main(){ int a = 2; int b = 3; int sum = add(a, b); printf("a=%d, b=%d, a+b=%d\n", a, b, sum); } 头文件定义: // mymath.h #ifndef MYMATH_H #define MYMATH_H int add(int a, int b); int sum(int a, int b); #endif 头文件实现: // mymath.c int add(int a, int b){ return a+b; } int sub(int a, int b){ return a-b; }
预处理阶段
预处理用于扩展源代码,插入全部的#include命令指定的文件,并扩展全部用#define声明指定的宏。预处理以后获得的仍然是文本文件,但文件体积会大不少。gcc的预处理是预处理器cpp来完成的,你能够经过以下命令对test.c进行预处理:
gcc -E -I./inc test.c -o test.i
或者直接调用cpp命令
cpp test.c -I./inc -o test.i
上述命令中-E是让编译器在预处理以后就退出,不进行后续编译过程;-I指定头文件目录,这里指定的是咱们自定义的头文件目录;-o指定输出文件名。
编译(Compilation)阶段
gcc -S -I./inc test.c -o test.s
上述命令中-S让编译器在编译以后中止,不进行后续过程。编译过程完成后,将生成程序的汇编代码test.s,这也是文本文件,内容以下:
// test.c汇编以后的结果test.s .file "test.c" .section .rodata .LC0: .string "a=%d, b=%d, a+b=%d\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp movl $2, 20(%esp) movl $3, 24(%esp) movl 24(%esp), %eax movl %eax, 4(%esp) movl 20(%esp), %eax movl %eax, (%esp) call add movl %eax, 28(%esp) movl 28(%esp), %eax movl %eax, 12(%esp) movl 24(%esp), %eax movl %eax, 8(%esp) movl 20(%esp), %eax movl %eax, 4(%esp) movl $.LC0, (%esp) call printf leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" .section .note.GNU-stack,"",@progbits
汇编(Assemble)阶段
汇编过程将上一步的汇编代码转换成机器码(machine code),这一步产生的文件叫作目标文件,是二进制格式。gcc汇编过程经过as命令完成:
$ as test.s -o test.o
等价于:
gcc -c test.s -o test.o
这一步会为每个源文件产生一个目标文件。所以mymath.c也须要产生一个mymath.o文件
连接(Linking)阶段
连接过程将多个目标文以及所需的库文件(.so等)连接成最终的可执行文件(executable file)。
命令大体以下:
$ ld -o test.out test.o inc/mymath.o ...libraries...
PHP:执行时编译为opcode,而后zend引擎执行opcode
Java:先编译成字节码,而后由JVM虚拟机执行字节码
C:直接编译成可执行文件,而后由操做系统执行能够行文件
参考资料:
http://tina.reeze.cn/book/
http://www.laruence.com/2008/...
http://rednaxelafx.iteye.com/...
http://www.vcgood.com/archive...
http://www.cnblogs.com/Carpen...
http://blog.csdn.net/cutesour...
http://www.nowamagic.net/libr...