1.EG(executor_globals/zend_executor_globals)
PHP整個(gè)生命周期中最主要的一個(gè)結(jié)構(gòu),是一個(gè)全局變量,在main執(zhí)行前分配(非ZTS下),直到PHP退出,它記錄著當(dāng)前請(qǐng)求全部的信息
2.EX(execute_data/zend_execute_data)
在執(zhí)行過程中最核心的一個(gè)結(jié)構(gòu),每次函數(shù)的調(diào)用、include/require、eval等都會(huì)生成一個(gè)新的結(jié)構(gòu),它表示當(dāng)前的作用域、代碼的執(zhí)行位置以及局部變量的分配等等,
#define EX(element) ((execute_data)->element) struct _zend_execute_data { const zend_op *opline; //指向當(dāng)前執(zhí)行的opcode,初始時(shí)指向zend_op_array起始位置 zend_execute_data *call; /* current call */ zval *return_value; //返回值指針 zend_function *func; //當(dāng)前執(zhí)行的函數(shù)(非函數(shù)調(diào)用時(shí)為空) zend_class_entry *called_scope; //當(dāng)前call的類 zend_execute_data *prev_execute_data; //函數(shù)調(diào)用時(shí)指向調(diào)用位置作用空間 zend_array *symbol_table; //全局變量符號(hào)表 #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; //字面量數(shù)組,與func.op_array->literals相同 #endif };
3.Zend的執(zhí)行流程
在Zend VM中zend_execute_data的zend_execute_data.opline,zend_execute_data.prev_execute_data,實(shí)現(xiàn)了call/ret,后面會(huì)分配額外的內(nèi)存空間用于局部變量的存儲(chǔ),實(shí)現(xiàn)了ebp/esp的作用。
step1: 為當(dāng)前作用域分配一塊內(nèi)存,充當(dāng)運(yùn)行棧,zend_execute_data結(jié)構(gòu)、所有局部變量、中間變量等等都在此內(nèi)存上分配 step2: 初始化全局變量符號(hào)表,然后將全局執(zhí)行位置指針EG(current_execute_data)指向step1新分配的zend_execute_data,然后將zend_execute_data.opline指向op_array的起始位置 step3: 從EX(opline)開始調(diào)用各opcode的C處理handler(即_zend_op.handler),每執(zhí)行完一條opcode將EX(opline)++繼續(xù)執(zhí)行下一條,直到執(zhí)行完全部opcode,函數(shù)調(diào)用、if的執(zhí)行過程: step3.1: if語(yǔ)句將根據(jù)條件的成立與否決定EX(opline) + offset所加的偏移量,實(shí)現(xiàn)跳轉(zhuǎn) step3.2: 如果是函數(shù)調(diào)用,則首先從EG(function_table)中根據(jù)function_name取出此function對(duì)應(yīng)的編譯完成的zend_op_array,然后像step1一樣新分配一個(gè)zend_execute_data結(jié)構(gòu), 將EG(current_execute_data)賦值給新結(jié)構(gòu)的prev_execute_data,再將EG(current_execute_data)指向新的zend_execute_data,最后從新的zend_execute_data.opline開始執(zhí)行,切換 到函數(shù)內(nèi)部,函數(shù)執(zhí)行完以后將EG(current_execute_data)重新指向EX(prev_execute_data),釋放分配的運(yùn)行棧,銷毀局部變量,繼續(xù)從原來函數(shù)調(diào)用的位置執(zhí)行 step4: 全部opcode執(zhí)行完成后將step1分配的內(nèi)存釋放,這個(gè)過程會(huì)將所有的局部變量"銷毀",執(zhí)行階段結(jié)束
4.運(yùn)行時(shí)緩存
在執(zhí)行期間,PHP經(jīng)常需要根據(jù)名稱去不同的哈希表中查找常量、函數(shù)、類、成員方法、成員屬性等,因此PHP提供了一種緩存機(jī)制用于緩存根據(jù)名稱查找到的結(jié)果,以便再次執(zhí)行同一opcode時(shí)直接復(fù)用上次緩存的值,無(wú)需重復(fù)查找,從而提高執(zhí)行效率。運(yùn)行時(shí)緩存機(jī)制是在同一opcode執(zhí)行多次的情況下才會(huì)生效,特別注意這里的同一opcode指的并不是opcode值相同,而是指內(nèi)存里的同一份數(shù)據(jù)
實(shí)際上運(yùn)行時(shí)緩存是基于所屬opcode中CONST操作數(shù)存儲(chǔ)的,也就是說只有包含IS_CONST類型的操作數(shù)才有可能用到此機(jī)制,其它類型都不會(huì)用到,這是因?yàn)橹挥蠧ONST操作數(shù)是固定不變的,其它CV、VAR等類型值都不是固定的,既然其值是不固定的那么緩存的值也就不是固定的,所以不會(huì)針對(duì)CONST以外類型的opcode操作進(jìn)行緩存
緩存的存儲(chǔ)格式是一個(gè)數(shù)組,用于保存緩存的數(shù)據(jù)指針,而指針在數(shù)組中的起始存儲(chǔ)位置則保存在CONST操作數(shù)對(duì)應(yīng)的zval.u2.cache_slot中,實(shí)際上它是在編譯階段確定的,通過zend_op_array.cache_size記錄緩存可用起始位置,編譯過程中如果發(fā)現(xiàn)當(dāng)前操作適用緩存機(jī)制,則根據(jù)緩存數(shù)據(jù)的大小從cache_size開始分配8或16字節(jié)給那個(gè)操作數(shù),cache_size向后移動(dòng)對(duì)應(yīng)大小,然后將起始位置保存于CONST操作數(shù)的zval.u2.cache_slot中,執(zhí)行時(shí)直接根據(jù)這個(gè)值確定緩存位置。
緩存數(shù)據(jù)空間大小: 8字節(jié):常量、函數(shù)、類 16字節(jié):成員屬性、成員方法、類常量
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28588.html
摘要:引擎中定義了很多內(nèi)部函數(shù)供用戶在中使用,比如等等,除了引擎中定義的內(nèi)部函數(shù),擴(kuò)展中也提供了大量?jī)?nèi)部函數(shù),我們也可以靈活的通過擴(kuò)展自行定制。頭部是一個(gè)與完全相同的結(jié)構(gòu)函數(shù)指針,展開 1.函數(shù)的存儲(chǔ)結(jié)構(gòu) typedef union _zend_function zend_function; union _zend_function { zend_uchar typ...
摘要:代碼的編譯的解析過程任務(wù)就是將代碼轉(zhuǎn)化為數(shù)組,代碼里的所有信息都保存在數(shù)組中,然后將數(shù)組交給引擎執(zhí)行,就是內(nèi)核具體執(zhí)行的命令,比如賦值加減操作函數(shù)調(diào)用等,每一條都對(duì)應(yīng)一個(gè)處理,這些是提前定義好的函數(shù)。 1.PHP代碼的編譯 PHP的解析過程任務(wù)就是將PHP代碼轉(zhuǎn)化為opcode數(shù)組,代碼里的所有信息都保存在opcode數(shù)組中,然后將opcode數(shù)組交給zend引擎執(zhí)行,opcode就是...
摘要:插入一個(gè)元素時(shí)先將元素按先后順序插入數(shù)組,位置是,再根據(jù)的哈希值映射到散列表中的某個(gè)位置,將存入這個(gè)位置查找時(shí)先在散列表中映射到,得到在數(shù)組的位置,再?gòu)臄?shù)組中取出元素。目前只有兩種類型會(huì)使用這種機(jī)制。 1.變量結(jié)構(gòu) typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:父類方法為錯(cuò)誤,成員方法不得被重寫。父子類方法靜態(tài)屬性不一致父類方法為非靜態(tài)而子類的是靜態(tài)或相反,錯(cuò)誤。 1.類的結(jié)構(gòu) 類是編譯階段的產(chǎn)物,而對(duì)象是運(yùn)行時(shí)產(chǎn)生的,它們歸屬于不同階段。編譯完成后我們定義的每個(gè)類都會(huì)生成一個(gè)zend_class_entry,它保存著類的全部信息,在執(zhí)行階段所有類相關(guān)的操作都是用的這個(gè)結(jié)構(gòu), struct _zend_class_entry { ch...
摘要:局部變量中局部變量分配在結(jié)構(gòu)上,每次執(zhí)行都會(huì)生成一個(gè)新的,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時(shí)釋放,這是局部變量的生命周期。 1.局部變量 PHP中局部變量分配在zend_execute_data結(jié)構(gòu)上,每次執(zhí)行zend_op_array都會(huì)生成一個(gè)新的zend_execute_data,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時(shí)釋放,這是局部變量的生命周期。 讀寫操作:局部變量通過...
閱讀 3582·2021-09-22 15:50
閱讀 3262·2019-08-30 15:54
閱讀 2784·2019-08-30 14:12
閱讀 3084·2019-08-30 11:22
閱讀 2111·2019-08-29 11:16
閱讀 3605·2019-08-26 13:43
閱讀 1223·2019-08-23 18:33
閱讀 945·2019-08-23 18:32