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.運行時緩存
在執行期間,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字節:成員屬性、成員方法、類常量
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28588.html
摘要:引擎中定義了很多內部函數供用戶在中使用,比如等等,除了引擎中定義的內部函數,擴展中也提供了大量內部函數,我們也可以靈活的通過擴展自行定制。頭部是一個與完全相同的結構函數指針,展開 1.函數的存儲結構 typedef union _zend_function zend_function; union _zend_function { zend_uchar typ...
摘要:代碼的編譯的解析過程任務就是將代碼轉化為數組,代碼里的所有信息都保存在數組中,然后將數組交給引擎執行,就是內核具體執行的命令,比如賦值加減操作函數調用等,每一條都對應一個處理,這些是提前定義好的函數。 1.PHP代碼的編譯 PHP的解析過程任務就是將PHP代碼轉化為opcode數組,代碼里的所有信息都保存在opcode數組中,然后將opcode數組交給zend引擎執行,opcode就是...
摘要:插入一個元素時先將元素按先后順序插入數組,位置是,再根據的哈希值映射到散列表中的某個位置,將存入這個位置查找時先在散列表中映射到,得到在數組的位置,再從數組中取出元素。目前只有兩種類型會使用這種機制。 1.變量結構 typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:父類方法為錯誤,成員方法不得被重寫。父子類方法靜態屬性不一致父類方法為非靜態而子類的是靜態或相反,錯誤。 1.類的結構 類是編譯階段的產物,而對象是運行時產生的,它們歸屬于不同階段。編譯完成后我們定義的每個類都會生成一個zend_class_entry,它保存著類的全部信息,在執行階段所有類相關的操作都是用的這個結構, struct _zend_class_entry { ch...
摘要:局部變量中局部變量分配在結構上,每次執行都會生成一個新的,局部變量在執行之初分配,然后在執行結束時釋放,這是局部變量的生命周期。 1.局部變量 PHP中局部變量分配在zend_execute_data結構上,每次執行zend_op_array都會生成一個新的zend_execute_data,局部變量在執行之初分配,然后在執行結束時釋放,這是局部變量的生命周期。 讀寫操作:局部變量通過...
閱讀 3528·2021-09-22 15:50
閱讀 3233·2019-08-30 15:54
閱讀 2748·2019-08-30 14:12
閱讀 3058·2019-08-30 11:22
閱讀 2079·2019-08-29 11:16
閱讀 3574·2019-08-26 13:43
閱讀 1192·2019-08-23 18:33
閱讀 920·2019-08-23 18:32