(PHP7內核剖析-7) Zend引擎執行過程

1.EG(executor_globals/zend_executor_globals)數組

PHP整個生命週期中最主要的一個結構,是一個全局變量,在main執行前分配(非ZTS下),直到PHP退出,它記錄着當前請求所有的信息

圖片描述


2.EX(execute_data/zend_execute_data)緩存

在執行過程當中最核心的一個結構,每次函數的調用、include/require、eval等都會生成一個新的結構,它表示當前的做用域、代碼的執行位置以及局部變量的分配等等,
#define EX(element)             ((execute_data)->element)

struct _zend_execute_data {
    const zend_op       *opline;  //指向當前執行的opcode,初始時指向zend_op_array起始位置
    zend_execute_data   *call;             /* current call                   */
    zval                *return_value;  //返回值指針
    zend_function       *func;          //當前執行的函數(非函數調用時爲空)
    zend_class_entry    *called_scope;  //當前call的類
    zend_execute_data   *prev_execute_data; //函數調用時指向調用位置做用空間
    zend_array          *symbol_table; //全局變量符號表
#if ZEND_EX_USE_RUN_TIME_CACHE
    void               **run_time_cache;   /* cache op_array->run_time_cache */
#endif
#if ZEND_EX_USE_LITERALS
    zval                *literals;  //字面量數組,與func.op_array->literals相同
#endif
};

圖片描述


3.Zend的執行流程函數

在Zend VM中zend_execute_data的zend_execute_data.opline,zend_execute_data.prev_execute_data,實現了call/ret,後面會分配額外的內存空間用於局部變量的存儲,實現了ebp/esp的做用。
step1: 爲當前做用域分配一塊內存,充當運行棧,zend_execute_data結構、全部局部變量、中間變量等等都在此內存上分配
step2: 初始化全局變量符號表,而後將全局執行位置指針EG(current_execute_data)指向step1新分配的zend_execute_data,而後將zend_execute_data.opline指向op_array的起始位置
step3: 從EX(opline)開始調用各opcode的C處理handler(即_zend_op.handler),每執行完一條opcode將EX(opline)++繼續執行下一條,直到執行徹底部opcode,函數調用、if的執行過程:
step3.1: if語句將根據條件的成立與否決定EX(opline) + offset所加的偏移量,實現跳轉
step3.2: 若是是函數調用,則首先從EG(function_table)中根據function_name取出此function對應的編譯完成的zend_op_array,而後像step1同樣新分配一個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),釋放分配的運行棧,銷燬局部變量,繼續從原來函數調用的位置執行
step4: 所有opcode執行完成後將step1分配的內存釋放,這個過程會將全部的局部變量"銷燬",執行階段結束

圖片描述


4.運行時緩存ui

在執行期間,PHP常常須要根據名稱去不一樣的哈希表中查找常量、函數、類、成員方法、成員屬性等,所以PHP提供了一種緩存機制用於緩存根據名稱查找到的結果,以便再次執行同一opcode時直接複用上次緩存的值,無需重複查找,從而提升執行效率。運行時緩存機制是在同一opcode執行屢次的狀況下才會生效,特別注意這裏的同一opcode指的並非opcode值相同,而是指內存裏的同一份數據
實際上運行時緩存是基於所屬opcode中CONST操做數存儲的,也就是說只有包含IS_CONST類型的操做數纔有可能用到此機制,其它類型都不會用到,這是由於只有CONST操做數是固定不變的,其它CV、VAR等類型值都不是固定的,既然其值是不固定的那麼緩存的值也就不是固定的,因此不會針對CONST之外類型的opcode操做進行緩存
緩存的存儲格式是一個數組,用於保存緩存的數據指針,而指針在數組中的起始存儲位置則保存在CONST操做數對應的zval.u2.cache_slot中,實際上它是在編譯階段肯定的,經過zend_op_array.cache_size記錄緩存可用起始位置,編譯過程當中若是發現當前操做適用緩存機制,則根據緩存數據的大小從cache_size開始分配8或16字節給那個操做數,cache_size向後移動對應大小,而後將起始位置保存於CONST操做數的zval.u2.cache_slot中,執行時直接根據這個值肯定緩存位置。
緩存數據空間大小:
    8字節:常量、函數、類
    16字節:成員屬性、成員方法、類常量
相關文章
相關標籤/搜索