在wiki中[虛擬機](http://zh.wikipedia.org/wiki/虛擬機)的定義是:虛擬機(Virtual Machine),在計算機科學中的體系結構里,是指一種特殊的軟件,他可以在計算機平臺和終端用戶之間創建一種環境,而終端用戶則是基于這個軟件所創建的環境來操作軟件。在計算機科學中,虛擬機是指可以像真實機器一樣運行程序的計算機的軟件實現。
虛擬機是一種抽象的計算機,它有自己的指令集,有自己的內存管理體系。在此類虛擬機上實現的語言比較低抽象層次的語言更加明了,更加簡單易學。
## 虛擬機的類型[]()
虛擬機是一種抽象的計算機,是對真實計算機的虛擬和模擬,現在的計算機有不同的指令集架構([ISA: Instruction Set Architecture](http://homedir.jct.ac.il/~citron/ca/isa.html)),ISA是處理的一個部分,不同的處理器會有不同的架構,最常見的有3種:
- 基于棧的[Stack Machines](http://en.wikipedia.org/wiki/Stack_machine): 操作數保存在棧上。而不是使用寄存器來保存,現在很少有真實機器采用這個模型。對于虛擬機來說因為指令空間占用少,并且實現簡單,很多虛擬機采用這種模型,比如:JVM,HHVM等。
- 基于累加器的Accumulator Machines。這個模型使用稱作累加器(Accumulator)的的寄存器來保存一個操作數以及操作的結果
- 基于通用寄存器的General-Purpose-Register Machines,這些寄存器沒有特殊的用途。編譯器可以將操作數保存在這些寄存器中。ZendVM采用的就是基于寄存器的架構。
## Zend虛擬機核心實現代碼[]()
為了方便讀者對Zend引擎的實現有個全面的感覺,下面列出涉及到Zend引擎實現的核心代碼文件功能參考。
Zend引擎的核心文件都在$PHP_SRC/Zend/目錄下面。不過最為核心的文件只有如下幾個:
1. PHP語法實現
- Zend/zend_language_scanner.l
- Zend/zend_language_parser.y
1. Opcode編譯
- Zend/zend_compile.c
1. 執行引擎
- Zend/zend_vm_*
- Zend/zend_execute.c
## Zend虛擬機體系結構[]()
從概念層將Zend虛擬機的實現進行抽象,我們可以將Zend虛擬機的體系結構分為:解釋層、執行引擎、中間數據層,如圖7.1所示:

圖7.1 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的語法規則和一些內部約定生成中間代碼,并且這些中間代碼還需要依靠一些全局變量中轉數據和關聯。至于生成的中間代碼的執行過程是依據中間代碼的順利,依賴于執行過程中的全局變量,一步步執行。當然,在遇到一些函數跳轉也會發生偏移,但是最終還是會回到偏移點。
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊