php运行汇编,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
總結
以上是生活随笔為你收集整理的php运行汇编,php脚本的执行过程(编译与执行相分离)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小米相册-编辑 V1.0.3.0.6 发
- 下一篇: 全球独家首发!一加Ace 2V支持“主动