设为首页 收藏本站
查看: 1466|回复: 0

[经验分享] php脚本的执行过程(编译与执行相分离)

[复制链接]

尚未签到

发表于 2017-4-5 12:15:07 | 显示全部楼层 |阅读模式
  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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-360614-1-1.html 上篇帖子: PHP生成静态页面代码和调用方法 下篇帖子: PHP 执行系统外部命令 system() exec() passthru()
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表