PHP內核探索:Zend虛擬機

經過前面的學習,咱們瞭解到一個PHP文件在服務器端的執行過程包括如下兩個大的過程: php

  1. 遞給php程序須要執行的文件, php程序完成基本的準備工做後啓動PHP及Zend引擎, 加載註冊的擴展模塊。
  2. 初始化完成後讀取腳本文件,Zend引擎對腳本文件進行詞法分析,語法分析。而後編譯成opcode執行。 如過安裝了apc之類的opcode緩存, 編譯環節可能會被跳過而直接從緩存中讀取opcode執行。

在第二步中,詞法分析、語法分析,編譯中間代碼,執行中間代碼等各個部分統稱爲Zend虛擬機。 與Java、C#等編譯型語言相比,PHP少了一個手動編譯的過程,它們無需編譯便可運行,咱們稱其爲解釋性語言。 Java有本身的Java虛擬機,它在多個平臺上實現統一語言; C#有本身的.NET虛擬機,它在單一平臺實現多種語言; PHP跟他們同樣,也有屬於本身的Zend虛擬機。它們在本質是相同的,它們都是抽象的計算機。 這些虛擬機都是在某種較底層的語言上抽象出另一種語言,有本身的指令集,有本身的內存管理體系。 它們最終都會將抽象級別較高的語言實現轉化爲抽象級別較低的語言實現, 而且實現其它輔助功能,如內存管理,垃圾回收等機制, 以減小程序員在具體實現上的工做,從而能夠將更多的時間和精力投入到業務邏輯中。 從抽象層次看,Zend虛擬機比Java等語言更高級一些,這裏的高級不是說功能更強大或效率更高, 簡單點說,Zend虛擬機離真正的機器實現更遠一些。 最近這些年,語言的發展只是不斷的抽象,不斷的遠離機器,沒有根本性的變化。 程序員

這裏咱們從虛擬機的前世此生講起,敘述Zend虛擬機的實現原理,關鍵的數據結構, 並其中穿插一個關於語法實現的示例和源碼加密解密的過程說明。 算法

在wiki中虛擬機的定義是: 虛擬機(Virtual Machine),在計算機科學中的體系結構裏,是指一種特殊的軟件, 他能夠在計算機平臺和終端用戶之間建立一種環境,而終端用戶則是基於這個軟件所建立的環境來操做軟件。 在計算機科學中,虛擬機是指能夠像真實機器同樣運行程序的計算機的軟件實現。 數組

虛擬機是一種抽象的計算機,它有本身的指令集,有本身的內存管理體系。 在此類虛擬機上實現的語言比較低抽象層次的語言更加明瞭,更加簡單易學。 緩存

PHP文件是如何被解析的,生成的中間代碼表示什麼,生成的中間代碼與實際的PHP代碼是如何對應的,生成的中間代碼如何被執行的? 在執行過程當中會將會哪些中間的數據?整個虛擬機是否能夠優化?如何優化? 服務器

Zend虛擬機體系結構

從概念層將Zend虛擬機的實現進行抽象,咱們能夠將Zend虛擬機的體系結構分爲:解釋層、執行引擎、中間數據層。 數據結構

Zend虛擬機體系結構圖 閉包

當一段PHP代碼進入Zend虛擬機,它會被執行兩步操做:編譯和執行。 對於一個解釋性語言來講,這是一個創造性的舉動,可是,如今的實現並不完全。 如今當PHP代碼進入Zend虛擬機後,它雖然會被執行這兩步操做,可是這兩步操做對於一個常規的執行過程來講倒是連續的, 也就是說它並無轉變成和Java這種編譯型語言同樣:生成一箇中間文件存放編譯後的結果。 若是每次執行這樣的操做,對於PHP腳本的性能來講是一個極大的損失。 雖然有相似於APC,eAccelerator等緩存解決方案。可是其本質上是沒有變化的,而且不能將兩個步驟分離,各自發展壯大。 函數

解釋層

解釋層是Zend虛擬機執行編譯過程的位置。它包括詞法解析、語法解析和編譯生成中間代碼三個部分。 詞法分析就是將咱們要執行的PHP源文件,去掉空格,去掉註釋,切分爲一個個的標記(token), 而且處理程序的層級結構(hierarchical structure)。 源碼分析

語法分析就是將接受的標記(token)序列,根據定義的語法規則,來執行一些動做,Zend虛擬機如今使用的Bison使用巴科斯範式(BNF) 來描述語法。 編譯生成中間代碼是根據語法解析的結果對照Zend虛擬機制定的opcode生成中間代碼, 在PHP5.3.1中,Zend虛擬機支持135條指令(見Zend/zend_vm_opcodes.h文件), 不管是簡單的輸出語句仍是程序複雜的遞歸調用,Zend虛擬機最終都會將全部咱們編寫的PHP代碼轉化成這135條指令的序列, 以後在執行引擎中按順序執行。

中間數據層

當Zend虛擬機執行一個PHP代碼時,它須要內存來存儲許多東西, 好比,中間代碼,PHP自帶的函數列表,用戶定義的函數列表,PHP自帶的類,用戶自定義的類, 常量,程序建立的對象,傳遞給函數或方法的參數,返回值,局部變量以及一些運算的中間結果等。 咱們把這些全部的存放數據的地方稱爲中間數據層。

若是PHP以mod擴展的方式依附於Apache2服務器運行,中間數據層的部分數據可能會被多個線程共享,若是PHP自帶的函數列表等。 若是隻考慮單個進程的方式,當一個進程被建立時它就會被加載PHP自帶的各類函數列表,類列表,常量列表等。 當解釋層將PHP代碼編譯完成後,各類用戶自定義的函數,類或常量會添加到以前的列表中, 只是這些函數在其自身的結構中某些字段的賦值是不同的。

當執行引擎執行生成的中間代碼時,會在Zend虛擬機的棧中添加一個新的執行中間數據結構(zend_execute_data), 它包括當前執行過程的活動符號列表的快照、一些局部變量等。

執行引擎

Zend虛擬機的執行引擎是一個很是簡單的實現,它只是依據中間代碼序列(EX(opline)),一步一步調用對應的方法執行。 在執行引擎中沒並有相似於PC寄存器同樣的變量存放下一條指令,當Zend虛擬機執行到某條指令時,當它全部的任務都執行完了, 這條指令會本身調用下一條指令,即將序列的指針向前移動一個位置,從而執行下一條指令,而且在最後執行return語句,如此反覆。 這在本質上是一個函數嵌套調用。

回到開頭的問題,PHP經過詞法分析、語法分析和中間代碼生成三個步驟後,PHP文件就會被解析成PHP的中間代碼opcode。 生成的中間代碼與實際的PHP代碼之間並無徹底的一一對應關係。只是針對用戶所給的PHP代碼和PHP的語法規則和一些內部約定生成中間代碼, 而且這些中間代碼還須要依靠一些全局變量中轉數據和關聯。至於生成的中間代碼的執行過程是依據中間代碼的順利, 依賴於執行過程當中的全局變量,一步步執行。固然,在遇到一些函數跳轉也會發生偏移,可是最終仍是會回到偏移點。

延伸閱讀

此文章所在專題列表以下:

  1. PHP內核探索:從SAPI接口開始
  2. PHP內核探索:一次請求的開始與結束
  3. PHP內核探索:一次請求生命週期
  4. PHP內核探索:單進程SAPI生命週期
  5. PHP內核探索:多進程/線程的SAPI生命週期
  6. PHP內核探索:Zend引擎
  7. PHP內核探索:再次探討SAPI
  8. PHP內核探索:Apache模塊介紹
  9. PHP內核探索:經過mod_php5支持PHP
  10. PHP內核探索:Apache運行與鉤子函數
  11. PHP內核探索:嵌入式PHP
  12. PHP內核探索:PHP的FastCGI
  13. PHP內核探索:如何執行PHP腳本
  14. PHP內核探索:PHP腳本的執行細節
  15. PHP內核探索:操做碼OpCode
  16. PHP內核探索:PHP裏的opcode
  17. PHP內核探索:解釋器的執行過程
  18. PHP內核探索:變量概述
  19. PHP內核探索:變量存儲與類型
  20. PHP內核探索:PHP中的哈希表
  21. PHP內核探索:理解Zend裏的哈希表
  22. PHP內核探索:PHP哈希算法設計
  23. PHP內核探索:翻譯一篇HashTables文章
  24. PHP內核探索:哈希碰撞攻擊是什麼?
  25. PHP內核探索:常量的實現
  26. PHP內核探索:變量的存儲
  27. PHP內核探索:變量的類型
  28. PHP內核探索:變量的值操做
  29. PHP內核探索:變量的建立
  30. PHP內核探索:預約義變量
  31. PHP內核探索:變量的檢索
  32. PHP內核探索:變量的類型轉換
  33. PHP內核探索:弱類型變量的實現
  34. PHP內核探索:靜態變量的實現
  35. PHP內核探索:變量類型提示
  36. PHP內核探索:變量的生命週期
  37. PHP內核探索:變量賦值與銷燬
  38. PHP內核探索:變量做用域
  39. PHP內核探索:詭異的變量名
  40. PHP內核探索:變量的value和type存儲
  41. PHP內核探索:全局變量Global
  42. PHP內核探索:變量類型的轉換
  43. PHP內核探索:內存管理開篇
  44. PHP內核探索:Zend內存管理器
  45. PHP內核探索:PHP的內存管理
  46. PHP內核探索:內存的申請與銷燬
  47. PHP內核探索:引用計數與寫時複製
  48. PHP內核探索:PHP5.3的垃圾回收機制
  49. PHP內核探索:內存管理中的cache
  50. PHP內核探索:寫時複製COW機制
  51. PHP內核探索:數組與鏈表
  52. PHP內核探索:使用哈希表API
  53. PHP內核探索:數組操做
  54. PHP內核探索:數組源碼分析
  55. PHP內核探索:函數的分類
  56. PHP內核探索:函數的內部結構
  57. PHP內核探索:函數結構轉換
  58. PHP內核探索:定義函數的過程
  59. PHP內核探索:函數的參數
  60. PHP內核探索:zend_parse_parameters函數
  61. PHP內核探索:函數返回值
  62. PHP內核探索:形參return value
  63. PHP內核探索:函數調用與執行
  64. PHP內核探索:引用與函數執行
  65. PHP內核探索:匿名函數及閉包
  66. PHP內核探索:面向對象開篇
  67. PHP內核探索:類的結構和實現
  68. PHP內核探索:類的成員變量
  69. PHP內核探索:類的成員方法
  70. PHP內核探索:類的原型zend_class_entry
  71. PHP內核探索:類的定義
  72. PHP內核探索:訪問控制
  73. PHP內核探索:繼承,多態與抽象類
  74. PHP內核探索:魔術函數與延遲綁定
  75. PHP內核探索:保留類與特殊類
  76. PHP內核探索:對象
  77. PHP內核探索:建立對象實例
  78. PHP內核探索:對象屬性讀寫
  79. PHP內核探索:命名空間
  80. PHP內核探索:定義接口
  81. PHP內核探索:繼承與實現接口
  82. PHP內核探索:資源resource類型
  83. PHP內核探索:Zend虛擬機
  84. PHP內核探索:虛擬機的詞法解析
  85. PHP內核探索:虛擬機的語法分析
  86. PHP內核探索:中間代碼opcode的執行
  87. PHP內核探索:代碼的加密與解密
  88. PHP內核探索:zend_execute的具體執行過程
  89. PHP內核探索:變量的引用與計數規則
  90. PHP內核探索:新垃圾回收機制說明
相關文章
相關標籤/搜索