资讯专栏INFORMATION COLUMN

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

13651657101 / 1475人阅读

摘要:效率比较低,依赖解释器,跨平台性好语言编译执行过程下面都是鸟哥博客的内容深入理解原理之引擎对这个文件进行词法分析,语法分析,编译成,然后执行。

编译型语言和解释型语言

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

编译型语言

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

解释型语言

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

PHP语言编译执行过程

下面都是鸟哥博客的内容:深入理解PHP原理之opcode

hello.php
 

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转换成一个一个的简单的表达式

> 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 
#include "mymath.h"// 自定义头文件
int main(){
    int a = 2;
    int b = 3;
    int sum = add(a, b); 
    printf("a=%d, b=%d, a+b=%d
", 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
"
    .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...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/23230.html

相关文章

  • 开发语言PHPJavaC语言编译执行过程

    摘要:效率比较低,依赖解释器,跨平台性好语言编译执行过程下面都是鸟哥博客的内容深入理解原理之引擎对这个文件进行词法分析,语法分析,编译成,然后执行。 编译型语言和解释型语言 从PHP,Java和C语言的编译执行过程可以先解释下编译型语言和解释型语言。 编译型语言 程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了。程序执行效率高...

    gnehc 评论0 收藏0
  • 第2章:软件构建过程和工具 2.2软件构建过程,系统和工具

    摘要:建模语言建模语言是可用于表达信息或知识或系统的任何人造语言,该结构由一组一致的规则定义,目标是可视化,推理,验证和传达系统设计。将这些文件安排到不同的地方称为源代码树。源代码树的结构通常反映了软件的体系结构。 大纲 软件构建的一般过程: 编程/重构 审查和静态代码分析 调试(倾倒和记录)和测试 动态代码分析/分析 软件构建的狭义过程(Build): 构建系统:组件和过程 构建变体...

    godiscoder 评论0 收藏0
  • Hack on HHVM —— Facebook是如何优化PHP

    摘要:周四正式发布了编程语言,将静态类型以及一些现代的语言特性引入了。这是对优化之路上的新里程碑。但是语言层面的优化限制太多,对而言还是不够用。其次是优化运行的步骤。在这方面进行调整,可以提升运行的性能。值得注意的是,给的影响很大。 Facebook周四正式发布了Hack编程语言,将静态类型以及一些现代的语言特性引入了PHP。这是Facebook对PHP优化之路上的新里程碑。 showIm...

    lmxdawn 评论0 收藏0
  • 盘点 PHP 和 ASP.NET 10大对比!

    摘要:谷歌,,,雅虎和最近因世界杯获得庞大观众数量的都在使用。因此,数据库服务器的能力是毋庸置疑的。微软的服务器,服务器以及未来的更新价格昂贵。更依赖于微软数量有限的开发者做出的改进和更新。 【编者按】本文主要针对开源 PHP 和非开源的 ASP.NET 在性能、成本、可扩展性,技术支持和复杂性等方面进行比较。 在网上论坛,总是有成百上千的文章和帖子在讨论 PHP 和 ASP.NET,究竟谁...

    hosition 评论0 收藏0

发表评论

0条评论

13651657101

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<