php的zend引擎執行過程 一


1. Zend引擎主要包含兩個核心部分:編譯、執行:

                           

    執行階段主要用到的數據結構:

          opcode: php代碼編譯產生的zend虛擬機可識別的指令,php7有173個opcode,定義在 zend_vm_opcodes.hPHP中的所有語法實現都是由這些opcode組成的。

         

struct _zend_op {
    const void *handler; //對應執行的C語言function,即每條opcode都有一個C function處理
    znode_op op1;   //操作數1
    znode_op op2;   //操作數2
    znode_op result; //返回值
    uint32_t extended_value; 
    uint32_t lineno; 
    zend_uchar opcode;  //opcode指令
    zend_uchar op1_type; //操作數1類型
    zend_uchar op2_type; //操作數2類型
    zend_uchar result_type; //返回值類型
};

         zend_op_array : zend引擎執行階段的輸入數據結構,整個執行階段都是操作這個數據結構。

             

                            

 

 

             zend_op_array有三個核心部分:opcode指令(對應c的指令)

                                                   字面量存儲(變量初始值、調用的函數名稱、類名稱、常量名稱等等稱之為字面量)

                                                   變量分配的情況 (當前array定義的變量 臨時變量的數量 編號,執行初始化一次性分配zval,使用時完全按照標號索引不是根據變量名)

         

           zend_executor_globals     PHP整個生命周期中最主要的一個結構,是一個全局變量,在main執行前分配(非ZTS下),直到PHP退出,它記錄着當前請求全部的信息,經常見到的一個宏EG操作的就是這個結構。

                                定義在zend_globals.h中:

 

                                   

 

                

               zend_execute_data  是執行過程中最核心的一個結構,每次函數的調用、include/require、eval等都會生成一個新的結構,它表示當前的作用域、代碼的執行位置以及局部變量的分配等等,等同於機器碼執行過程中stack的角色,后面分析具體執行流程的時候會詳細分析其作用。 

              zend_execute_data與zend_op_array的關聯關系:

                                         

2.執行過程

        Zend的executor與linux二進制程序執行的過程是非常類似的。

        在C程序執行時有兩個寄存器ebp、esp分別指向當前作用棧的棧頂、棧底,局部變量全部分配在當前棧,函數調用、返回通過callret指令完成,調用時call將當前執行位置壓入棧中,返回時ret將之前執行位置出棧,跳回舊的位置繼續執行。

        Zend VM中zend_execute_data就扮演了這兩個角色,zend_execute_data.prev_execute_data保存的是調用方的信息,實現了call/retzend_execute_data后面會分配額外的內存空間用於局部變量的存儲,實現了ebp/esp的作用。

                    a. 為當前作用域分配一塊內存,充當運行棧,zend_execute_data結構、所有局部變量、中間變量等等都在此內存上分配

                    b.初始化全局變量符號表,然后將全局執行位置指針EG(current_execute_data)指向步驟a新分配的zend_execute_data,然后將zend_execute_data.opline指向op_array的起始位置

                    c.從EX(opline)開始調用各opcode的C處理handler(即_zend_op.handler),每執行完一條opcode將EX(opline)++繼續執行下一條,直到執行完全部opcode

                                if語句將根據條件的成立與否決定EX(opline) + offset所加的偏移量,實現跳轉

                                如果是函數調用,則首先從EG(function_table)中根據function_name取出此function對應的編譯完成的zend_op_array,然后像步驟a一樣新分配一個zend_execute_data結構,將EG(current_execute_data)賦值給新結構的prev_execute_data,再將EG(current_execute_data)指向新的zend_execute_data,最后從新的zend_execute_data.opline開始執行,切換到函數內部,函數執行完以后將EG(current_execute_data)重新指向EX(prev_execute_data),釋放分配的運行棧,銷毀局部變量,繼續從原來函數調用的位置執行

                                類方法的調用與函數基本相同

                    d.全部opcode執行完成后將步驟a分配的內存釋放,這個過程會將所有的局部變量"銷毀",執行階段結束

                                    

 

                              首先根據zend_execute_data、當前zend_op_array中局部/臨時變量數計算需要的內存空間,編譯階段zend_op_array的結果,在編譯過程中已經確定當前作用域下有多少個局部變量(func->op_array.last_var)、臨時/中間/無用變量(func->op_array.T),從而在執行之初就將他們全部分配完成。

            

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM