【开发语言】PHP、Java、C语言的编译执行过程

编译型语言和解释型语言

从PHP,Java和C语言的编译执行过程能够先解释下编译型语言和解释型语言。php

  • 编译型语言html

程序在执行以前须要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不须要从新翻译,直接使用编译的结果就好了。程序执行效率高,依赖编译器,跨平台性差些。如C、C++、Delphi等.java

  • 解释型语言express

程序不须要编译,程序在运行时才翻译成机器语言,每执行一次都要翻译一次。所以效率比较低。好比Basic语言,专门有一个解释器可以直接执行Basic程序,每一个语句都是执行的时候才翻译。(在运行程序的时候才翻译,专门有一个解释器去进行翻译,每一个语句都是执行的时候才翻译。效率比较低,依赖解释器,跨平台性好.)ubuntu

PHP语言编译执行过程

下面都是鸟哥博客的内容:深刻理解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

Java语言编译执行过程

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语言编译执行过程

参考原文: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...

相关文章
相关标签/搜索