php脚本的执行过程(编译与执行相分离)

     php的编译和执行是分离开的,亦即:先执行完编译,而后再执行。很多人会说:c++也是如此啊,确实。不过php的这种分离可以给我们提供很多便利,当然不可避免也有很有缺点。

 

      先说一下整个过程:

      ①php会调用编译函数zend_compile_file()来进行编译。 这个函数的具体实现其实是包括两个主要过程的:词法分析(Lex实现),语法分析(Yacc实现)。当执行完这个函数之后:php脚本的编译就算结束了。 这个函数的输入是:php脚本文件,而输出则是op_array.简单一点说:编译过程就是把脚本给解析成一条条php虚拟机可以处理的指令,而op_array就是这些指令做成的一个array而已(这很类似一些编译型语言编译产生的汇编代码了,也是一条条的命令)。

      ②:之后php虚拟机会调用zend_execute()这个函数来执行。该函数的输入就是上边编译阶段产生的op_array,在这里他会解析每条命令并进行处理。 由于op命令一共有150左右,所以它需要处理这150中命令。这里会产生一个很有意思的问题:它是如何处理这150种命令的呢?首先每条命令都是有对应的处理器来进行处理的。所以:虚拟机会依据op_array中各条命令的类型来分发给响应的处理器来进行处理。

      这里有两个小问题: 1:这里的处理器是什么?  2:如何分发的?

      要解答这两个问题都是要从分发机制上来解释:php虚拟机分发命令的机制有三种:CALL, SWITCH, 和GOTO这三种类型.php默认是使用CALL方式, 也就是所有的opcode处理器都定义为函数, 然后供虚拟机调用. 这种方式是传统的方式, 也一般被认为是最稳定的方式.而SWITCH方式和GOTO方式则是通过switch和goto来分发opcode到对应的处理逻辑(段)执行的.

      那现在来回答上边两个问题:

      1:处理器其实是处理op命令的逻辑。其可以以函数的形式存在,也可能是以逻辑段的方式存在,这取决于命令的分发方式。

      2:分发方式有call,switch和goto三种。哪种效率高呢?其实从上边解释已经可以初步了解了。switch和goto都是在zend_execute()这个函数中有对应的逻辑段,直接执行就可以了。而call是在zend_execute()这个函数中执行函数调用。明摆着:函数调用效率是最低的,调用一次就得压栈啊!所以效率上:call是最低的。对于switch和goto:比如要执行第三种命令的处理:switch还要先挨个判断是不是前两种,而goto根本不需要判断,直接跳到第三种命令的逻辑代码段去执行,这比switch少了顺序从上到下判断的损耗,所以:goto效率又比switch要高。  所以这三种分发方式总体而言:goto > switch > call

 

 

     题外话:由于php默认是call,如果你想进一步榨干php的效能,可以更改下其命令分发方式为goto。不过用goto方式虽然提高了执行速度,但是编译速度上其实最慢的喔。

 

--------------------------------------------------------------------------------------------------------------------------------------------------

     再说一下php这种编译和执行分离的弱点:

     其实也不能算是弱点,虽然zend engine(php的虚拟机)将编译和执行严格分开,但是对于用户而言:就跟没分开一样,因为我每次执行一个php脚本请求都是要执行:编译->执行  这两个阶段。任何一个阶段都少不了。那么这一点我们可以拿来和c++这种编译型语言做一下对比: 同一个请求运行100遍

     ①对于c++,由于其前期只要编译一遍,编译好就不会再重复编译了,只需要执行就ok,所以其损耗为:

         1次编译 + 100次执行

     ②对于php,其每次都要编译+执行,所以其损耗为:

         100次编译 + 100次执行

 

      显然:解释性语言从数量上来看:其消耗是比编译型语言多的多。说白了就是:php这种编译和执行相分离并不是真正的分离。而c++那种才算是真正的分离。

 

      php也早就意识到这个问题了,于是就想了一个办法来解决这个问题:这个解决方案就是eAccelerator。主要思路如下:

当脚本第一次运行后,以某种方式保存编译后脚本(里边存放的是op_array),在我们规定的缓存有效时间内,当第二次运行该脚本时就不在进行重复性的编译工作,而是直接调用执行前面保存的编译后文件,大大提高了程序性能。

 

     这种方式一定程度上提高了php的效率,但不是最终极的方法,最终极的还是改成编译型语言那种方式好了,吼吼~~~

 

---------------------------------------------------------------------------------------------------------------------------------------------------

    最后说一下php编译和执行分离的优点;

    这个优点其实是针对程序员而言,对用户而言没什么。因为这两个阶段的分离,我们可以在这里做一些我们想做的事情。

    比如想做文件加解密,你想把一些php脚本源码文件加密,让用户看不到源码。同时呢这个加密后的源码文件又可以被php虚拟机所解析和处理。当然:要实现这个前提是你先想好加解密算法并保证这个是可逆的过程。

 

    现在你对php源码文件已经加密了,此时你需要定义一下这种加密文件的后缀,假设为:*.buaa。 那问题就是:我们怎么让php虚拟机可以处理这种后缀的文件呢?这就要用到上边所说的编译和执行相分离的过程了。

    回想一下:编译阶段的输入是php源文件,输出是op_array。 ok,我们就在这个阶段做文章。主要思路为:首先在zend_compile_file()这个编译函数中:看一下输入文件的后缀:如果是正常的.php那就走正常逻辑,如果是*.buaa,那就先解密然后再走正常逻辑。。。

    哈~就是这么简单。当然:这个过程没有所说的这么简单,而且你也不可能直接修改zend_compile_file()函数,最后是自己扩展实现一个模块来处理这个过程。

 

 

 

本文参考了如下博文:

1:http://www.laruence.com/2008/08/14/250.html

2:http://yanbin.org/archive/zend-engines-fantasy.html

3:http://www.laruence.com/2008/06/18/221.html

4:http://www.laruence.com/2009/10/15/1131.html

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
迈思框架(MyStep Framework)是一套基于 [PHP 7.0] 的web开发框架,旨在构建一个可以便捷调用常用功能,以最简洁的代码实现目标功能,同时具备高度可扩展性,可通过代理模式,方便的将第三方功能模块集成到框架中。 MyStep Framework功能: 1、路由系统 - 框架通过 rewrite 方法接管所有响应,除 static 目录和自定义扩展类型外,其他文件均无法直接通过 url 访问,兼具高可控性和安全性。 (IIS对应web.config,Apache对应.htaccess,NginX需参考目录下文件手动添加)。 2、路由模式 - 为增加环境适应度,框架同时支持Rewrite,QueryString和PathInfo三种模式,页面中站内URL只需要按照rewrite的模式书写(对于框架目录,首位无需加"/"),框架将自动调整为对应模式,但为保证最大兼容性,php脚本内的链接多以QueryString模式处理。 3、模版系统 - 采用二次编译模式,严格实现模板与程序的分离,通过通俗的标签模式调用各类数据。基本模板格式简单易学,方便制作,只要对HTML有一定了解的设计师均可以很快上手,模板修改后即时生效。同时具备高度可扩展性,可根据实际需要任意扩充模版标签。 4、插件系统 - 可插件模式扩展框架功能,无论是功能增强、系统优化、前台展示均可与系统无缝连接。内容评分、评论、投票、专题、检索、采集、统计等都可以通过插件实现,并可以无缝结合到系统中。 5、应用接口 - 系统为各类插件提供了丰富的接口,无论是api、模板标签、代码嵌入、脚本附加、登录处理,都可以通过系统接口便捷地实现,为二次开发或插件开发提供最大限度的支持和自由。 6、多语言支持 - 系统可以随意添加语言包,通过调整参数立即变化。 7、缓存机制 - 通过三级缓存保证高效 数据缓存,用于缓存从数据库查询出的结果集,包含自建文件和数据库两种模式,也可通过代理模式扩展; 页面缓存,可将解析好的页面整体缓存到缓存文件,在过期前不用再次生成页面,即实现了静态化的效果,也保留了动态脚本的特性; 浏览器缓存,通过etag标识,在客户端再次请求页面数据时,如页面未发生变化,则直接从客户端缓存调用数据,减少了对服务器带宽的请求。
### 回答1: 要查看shell脚本执行过程,可以使用以下方法: 1. 在脚本中添加调试信息,例如echo语句,输出变量值等。 2. 在执行脚本时添加-v选项,例如sh -v script.sh,这样会显示每个命令的执行过程。 3. 在执行脚本时添加-x选项,例如sh -x script.sh,这样会显示每个命令的执行过程,并且会将变量展开。 4. 使用set命令设置调试选项,例如set -x,这样会在脚本中所有命令执行前都显示命令本身和参数。 以上是几种常见的查看shell脚本执行过程的方法,可以根据实际情况选择适合自己的方法。 ### 回答2: 在Linux和Unix操作系统中,Shell是一个重要的命令解释器,它能够方便地执行命令。Shell脚本是由多个Shell命令组成的集合,可以被解释执行。在编写Shell脚本时,有时候我们需要查看它的执行过程,以便更好地调试和优化代码。 下面介绍几种查看Shell脚本执行过程的方法: 1. 加入调试输出 在Shell脚本中加入调试输出可以方便我们查看执行情况。可以使用echo命令输出提示或变量值,或使用set -x命令开启调试模式,以输出详细的执行过程。如下示例: #!/bin/bash set -x echo "start" var=5 echo $var set +x echo "end" 在执行以上脚本时,会输出每一步的执行过程,如下所示: $ ./test.sh + echo start start + var=5 + echo 5 5 + set +x end 2. 使用trace模式 使用trace模式可以在执行Shell脚本时打印出每一行命令以及执行的结果。可以在执行脚本时加上“-o”和“-e”选项,分别指定输出和错误信息的文件名。如下示例: $ bash -x script.sh -o output.log -e error.log 此时执行的命令会输出到output.log文件中,错误信息会输出到error.log文件中。通过查看这两个文件,可以获得Shell脚本执行的详细信息。 3. 使用bashdb进行调试 bashdb是一个命令行调试器,可以用于调试Shell脚本。它可以在执行时停下来,允许程序员逐行地检查脚本中的变量、语句和流程。使用bashdb需要安装应的软件包,然后在执行脚本时指定“-x”选项即可。如下示例: $ bashdb -x script.sh 以上就是查看Shell脚本执行过程的几种方法,通过这些方法,可以更好地进行Shell脚本开发和调试。 ### 回答3: shell脚本是一种非交互式命令解释器,可以通过在控制台中输入一系列shell命令,将这些命令保存在脚本文件中,并通过执行脚本文件的方式来自动化执行一系列命令。在执行shell脚本过程中,我们可以查看脚本执行的详细过程,以便确定脚本是否按照预期执行。 为了查看shell脚本执行过程,我们可以采用以下方法: 1. 添加调试选项 在执行脚本时,可以通过在命令行中添加“-x”选项,来启用shell脚本的调试模式,这将显示每个执行的命令及其输出。例如: $ bash -x script.sh 2. 输出调试信息 可以在脚本中添加代码,以输出脚本执行过程的各个阶段的状态信息和变量值。例如,在脚本中添加以下代码: set -x # 开启调试模式 echo "Start executing script..." # 输出调试信息 var=10 # 定义变量 echo "var="$var # 输出变量值 set +x # 关闭调试模式 3. 使用调试器 还可以使用专门的shell脚本调试器来查看脚本执行过程。例如,bashdb是一个bash脚本调试器,它可以通过执行以下命令进行安装: $ sudo apt-get install bashdb 安装完成后,可以使用以下命令启动脚本调试器: $ bashdb script.sh 执行完以上步骤后,通过逐步执行脚本代码行来查看脚本执行的详细过程。这将有助于调试脚本中的错误或问题。 总之,以上方法可以帮助我们查看shell脚本执行过程,以便识别和解决脚本执行中的错误和问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值