亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

PHP7源碼:PHP虛擬機的詳細解析

2020-03-22 19:44:31
字體:
來源:轉載
供稿:網友
本篇文章給大家帶來的內容是關于PHP7源碼:PHP虛擬機詳細解析,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

1.從物理機說起

虛擬機也是計算機,設計思想和物理機有很多相似之處;

1.1馮諾依曼體系結構

馮·諾依曼是當之無愧的數字計算機之父,當前計算機都采用的是馮諾依曼體系結構;設計思想主要包含以下幾個方面:

指令和數據不加區別混合存儲在同一個存儲器中,它們都是內存中的數據?,F代CPU的保護模式,每個內存段都有段描述符,這個描述符記錄著這個內存段的訪問權限(可讀,可寫,可執行)。這就變相的指定了哪些內存中存儲的是指令哪些是數據);

存儲器是按地址訪問的線性編址的一維結構,每個單元的位數是固定的;

數據以二進制表示;

指令由操作碼和操作數組成。操作碼指明本指令的操作類型,操作數指明操作數本身或者操作數的地址。操作數本身并無數據類型,它的數據類型由操作碼確定;任何架構的計算機都會對外提供指令集合;

運算器通過執行指令直接發出控制信號控制計算機各項操作。由指令計數器指明待執行指令所在的內存地址。指令計數器只有一個,一般按順序遞增,但執行順序可能因為運算結果或當時的外界條件而改變;

2631839729-5b6a6ac8d5766_articlex.png

1.2匯編語言簡介

任何架構的計算機都會提供一組指令集合;

指令由操作碼和操作數組成;操作碼即操作類型,操作數可以是一個立即數或者一個存儲地址;每條指令可以有0、1或2個操作數;

指令就是一串二進制;匯編語言是二進制指令的文本形式;

push %ebxmov %eax, [%esp+8]mov %ebx, [%esp+12]add %eax, %ebxpop %ebx

push、mov、add、pop等就是操作碼;
%ebx寄存器;[%esp+12]內存地址;
操作數只是一塊可存取數據的存儲區;操作數本身并無數據類型,它的數據類型由操作碼確定;
如movb傳送字節,movw傳送字,movl傳送雙字等

1.3 函數調用棧

過程(函數)是對代碼的封裝,對外暴露的只是一組指定的參數和一個可選的返回值;可以在程序中不同的地方調用這個函數;假設過程P調用過程Q,Q執行后返回過程P;為了實現這一功能,需要考慮三點:

指令跳轉:進入過程Q的時候,程序計數器必須被設置為Q的代碼的起始地址;在返回時,程序計數器需要設置為P中調用Q后面那條指令的地址;

數據傳遞:P能夠向Q提供一個或多個參數,Q能夠向P返回一個值;

內存分配與釋放:Q開始執行時,可能需要為局部變量分配內存空間,而在返回前,又需要釋放這些內存空間;

大多數的語言過程調用都采用了棧數據結構提供的內存管理機制;如下圖所示:

6391936-5b6a6e2478332_articlex.png

函數的調用與返回即對應的是一系列的入棧與出棧操作;
函數在執行時,會有自己私有的棧幀,局部變量就是分配在函數私有棧幀上的;
平時遇到的棧溢出就是因為調用函數層級過深,不斷入棧導致的;

2.PHP虛擬機

虛擬機也是計算機,參考物理機的設計,設計虛擬機時,首先應該考慮三個要素:指令,數據存儲,函數棧幀;

下面從這三點詳細分析PHP虛擬機的設計思路;

2.1指

2.1.1 指令類型

任何架構的計算機都需要對外提供一組指令集,其代表計算機支持的一組操作類型;

PHP虛擬機對外提供186種指令,定義在zend_vm_opcodes.h文件中;

//加、減、乘、除等#define ZEND_ADD 1#define ZEND_SUB 2#define ZEND_MUL 3#define ZEND_p 4#define ZEND_MOD 5#define ZEND_SL 6#define ZEND_SR 7#define ZEND_CONCAT 8#define ZEND_BW_OR 9#define ZEND_BW_AND 10……………………

2.1.2 指令

2.1.2.1指令的表示

指令由操作碼和操作數組成;操作碼指明本指令的操作類型,操作數指明操作數本身或者操作數的地址;

PHP虛擬機定義指令格式為:操作碼 操作數1 操作數2 返回值;其使用結構體_zend_op表示一條指令:

struct _zend_op { const void *handler; //指針,指向當前指令的執行函數 znode_op op1; //操作數1  znode_op op2; //操作數2 znode_op result; //返回值 uint32_t extended_html' target='_blank'>value;//擴展 uint32_t lineno; //行號 zend_uchar opcode; //指令類型 zend_uchar op1_type; //操作數1的類型(此類型并不代表字符串、數組等數據類型;其表示此操作數是常量,臨時變量,編譯變量等) zend_uchar op2_type; //操作數2的類型 zend_uchar result_type; //返回值的類型};

2.1.2.2 操作數的表示

從上面可以看到,操作數使用結構體znode_op表示,定義如下:

constant、var、num等都是uint32_t類型的,這怎么表示一個操作數呢?(既不是指針不能代表地址,也無法表示所有數據類型);
其實,操作數大多情況采用的相對地址表示方式,constant等表示的是相對于執行棧幀首地址的偏移量;
另外,_znode_op結構體中有個zval *zv字段,其也可以表示一個操作數,這個字段是一個指針,指向的是zval結構體,PHP虛擬機支持的所有數據類型都使用zval結構體表示;

typedef union _znode_op { uint32_t constant; uint32_t var; uint32_t num; uint32_t opline_num; #if ZEND_USE_ABS_JMP_ADDR zend_op *jmp_addr; #else uint32_t jmp_offset; #endif #if ZEND_USE_ABS_CONST_ADDR zval *zv; #endif} znode_op;

2.2 數據存儲

PHP虛擬機支持多種數據類型:整型、浮點型、字符串、數組,對象等;PHP虛擬機如何存儲和表示多種數據類型?

2.1.2.2節指出結構體_znode_op代表一個操作數;操作數可以是一個偏移量(計算得到一個地址,即zval結構體的首地址),或者一個zval指針;PHP虛擬機使用zval結構體表示和存儲多種數據;

struct _zval_struct { zend_value value; //存儲實際的value值 union { struct { //一些標志位 ZEND_ENDIAN_LOHI_4( zend_uchar type, //重要;表示變量類型 zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { //其他有用信息 uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ } u2;};

zval.u1.type表示數據類型, zend_types.h文件定義了以下類型:

#define IS_UNDEF 0#define IS_NULL 1#define IS_FALSE 2#define IS_TRUE 3#define IS_LONG 4#define IS_DOUBLE 5#define IS_STRING 6#define IS_ARRAY 7#define IS_OBJECT 8#define IS_RESOURCE 9#define IS_REFERENCE 10…………

zend_value存儲具體的數據內容,結構體定義如下:

_zend_value占16字節內存;long、double類型會直接存儲在結構體;引用、字符串、數組等類型使用指針存儲;

代碼中根據zval.u1.type字段,判斷數據類型,以此決定操作_zend_value結構體哪個字段;

可以看出,字符串使用zend_string表示,數組使用zend_array表示…

typedef union _zend_value { zend_long lval;  double dval;  zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww;} zend_value;

如下圖為PHP7中字符串結構圖:

2081089099-5b6a6ef88f01a_articlex.png

2.3 再談指令

2.1.2.1指出,指令使用結構體_zend_op表示;其中最主要2個屬性:操作函數,操作數(兩個操作數和一個返回值);

操作數的類型(常量、臨時變量等)不同,同一個指令對應的handler函數也會不同;操作數類型定義在 Zend/zend_compile.h文件:

//常量#define IS_CONST (1 0)//臨時變量,用于操作的中間結果;不能被其他指令對應的handler重復使用#define IS_TMP_VAR (1 1)//這個變量并不是PHP代碼中聲明的變量,常見的是返回的臨時變量,比如$a=time(), 函數time返回值的類型就是IS_VAR,這種類型的變量是可以被其他指令對應的handler重復使用的#define IS_VAR (1 2)#define IS_UNUSED (1 3) /* Unused variable *///編譯變量;即PHP中聲明的變量;#define IS_CV (1 4) /* Compiled variable */

操作函數命名規則為:ZEND_[opcode]_SPEC_(操作數1類型)_(操作數2類型)_(返回值類型)_HANDLER

比如賦值語句就有以下多種操作函數:

ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_UNUSED_HANDLER,ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER,…

對于$a=1,其操作函數為: ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER;函數實現為:

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) USE_OPLINE zval *value; zval *variable_ptr; SAVE_OPLINE(); //獲取op2對應的值,也就是1 value = EX_CONSTANT(opline- op2); //在execute_data中獲取op1的位置,也就是$a(execute_data類似函數棧幀,后面詳細分析) variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline- op1.var); //賦值 value = zend_assign_to_variable(variable_ptr, value, IS_CONST); if (UNEXPECTED(0)) { ZVAL_COPY(EX_VAR(opline- result.var), value); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();}

2.4 函數棧幀

2.4.1指令集

上面分析了指令的結構與表示,PHP虛擬機使用_zend_op_array表示指令的集合:

struct _zend_op_array { ………… //last表示指令總數;opcodes為存儲指令的數組; uint32_t last; zend_op *opcodes; //變量類型為IS_CV的個數 int last_var; //變量類型為IS_VAR和IS_TEMP_VAR的個數 uint32_t T; //存放IS_CV類型變量的數組 zend_string **vars; ………… //靜態變量 HashTable *static_variables; //常量個數;常量數組 int last_literal; zval *literals;};

注意: last_var代表IS_CV類型變量的個數,這種類型變量存放在vars數組中;在整個編譯過程中,每次遇到一個IS_CV類型的變量(類似于$something),就會去遍歷vars數組,檢查是否已經存在,如果不存在,則插入到vars中,并將last_var的值設置為該變量的操作數;如果存在,則使用之前分配的操作數

2.4.2 函數棧幀

PHP虛擬機實現了與1.3節物理機類似的函數棧幀結構;

使用 _zend_vm_stack表示棧結構;多個棧之間使用prev字段形成單向鏈表;top和end指向棧低和棧頂,分別為zval類型的指針;

struct _zend_vm_stack { zval *top; zval *end; zend_vm_stack prev;};

考慮如何設計函數執行時候的幀結構:當前函數執行時,需要存儲函數編譯后的指令,需要存儲函數內部的局部變量等(2.1.2.2節指出,操作數使用結構體znode_op表示,其內部使用uint32_t表示操作數,此時表示的就是當前zval變量相對于當前函數棧幀首地址的偏移量);

PHP虛擬機使用結構體_zend_execute_data存儲當前函數執行所需數據;

struct _zend_execute_data { //當前指令指令 const zend_op *opline;  //當前函數執行棧幀 zend_execute_data *call;  //函數返回數據  zval *return_value; zend_function *func;  zval This; /* this + call_info + num_args */ //調用當前函數的棧幀  zend_execute_data *prev_execute_data; //符號表 zend_array *symbol_table;#if ZEND_EX_USE_RUN_TIME_CACHE void **run_time_cache; #endif#if ZEND_EX_USE_LITERALS //常量數組 zval *literals; #endif};

函數開始執行時,需要為函數分配相應的函數棧幀并入棧,代碼如下:

static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object) //計算當前函數棧幀需要內存空間大小 uint32_t used_stack = zend_vm_calc_used_stack(num_args, func); //根據棧幀大小分配空間,入棧 return zend_vm_stack_push_call_frame_ex(used_stack, call_info, func, num_args, called_scope, object);//計算函數棧幀大小static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, zend_function *func) //_zend_execute_data大?。?0字節/16字節=5)+參數數目 uint32_t used_stack = ZEND_CALL_FRAME_SLOT + num_args; if (EXPECTED(ZEND_USER_CODE(func- type))) { //當前函數臨時變量等數目 used_stack += func- op_array.last_var + func- op_array.T - MIN(func- op_array.num_args, num_args); //乘以16字節 return used_stack * sizeof(zval);static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object) //上一個函數棧幀地址 zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top); //移動函數調用棧top指針 EG(vm_stack_top) = (zval*)((char*)call + used_stack); //初始化當前函數棧幀 zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object); //返回當前函數棧幀首地址 return call;}

從上面分析可以得到函數棧幀結構圖如下所示:

1497251327-5b6a6fb3222d7_articlex.png

總結

PHP虛擬機也是計算機,有三點是我們需要重點關注的:指令集(包含指令處理函數)、數據存儲(zval)、函數棧幀;

此時虛擬機已可以接受指令并執行指令代碼;

但是,PHP虛擬機是專用執行PHP代碼的,PHP代碼如何能轉換為PHP虛擬機可以識別的指令呢——編譯;

PHP虛擬機同時提供了編譯器,可以將PHP代碼轉換為其可以識別的指令集合;

理論上你可以自定義任何語言,只要實現編譯器,能夠將你自己的語言轉換為PHP可以識別的指令代碼,就能被PHP虛擬機執行;

相關文章推薦:

PHP7.0和php7.1中的語法新特性的總結

PHP中如何將session存入數據庫并使用(附代碼)

PHP中時間函數strtotime() 函數的原理講解

以上就是PHP7源碼:PHP虛擬機的詳細解析的詳細內容,PHP教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美激情在线一区| 欧美日韩人人澡狠狠躁视频| 日韩美女在线播放| 精品欧美aⅴ在线网站| 18性欧美xxxⅹ性满足| 日韩视频免费中文字幕| 国产精品成人国产乱一区| 亚洲热线99精品视频| 久久久久久久久久国产精品| 欧美国产日韩精品| 中文字幕在线看视频国产欧美| 777国产偷窥盗摄精品视频| 久久久久久久爱| 久久久久久午夜| 精品少妇v888av| 日本最新高清不卡中文字幕| 永久免费毛片在线播放不卡| 91精品国产色综合| 精品国产乱码久久久久酒店| 欧美成人在线免费视频| 亚洲第一天堂av| 日韩欧美在线视频日韩欧美在线视频| 国产精品久久久久久久久久久不卡| 久久精品视频免费播放| 国内久久久精品| 日韩中文在线中文网三级| 中文国产成人精品久久一| 日韩在线观看高清| 日韩欧美国产免费播放| 亚洲女人被黑人巨大进入al| 国产一区av在线| 久热精品视频在线观看一区| 欧美激情啊啊啊| 国产成人精品av在线| 亚洲欧洲xxxx| 久久亚洲成人精品| 国产日韩在线精品av| 97免费中文视频在线观看| 黑人精品xxx一区| 91在线免费观看网站| 国产精品男女猛烈高潮激情| 色综合久久久久久中文网| 萌白酱国产一区二区| 中文字幕一区二区精品| 国产有码一区二区| 欧美性xxxx极品hd欧美风情| 国产原创欧美精品| 欧美日韩激情小视频| 久久精品视频导航| 久久精品成人欧美大片古装| 成人黄色免费片| 日韩欧美高清视频| 国产欧美日韩精品专区| 97视频免费看| 欧美国产日本高清在线| 久久久久久久久久久成人| 色七七影院综合| 日韩av电影中文字幕| 国内精品久久久久| 91精品久久久久久久久青青| 懂色aⅴ精品一区二区三区蜜月| 伊是香蕉大人久久| 久久久亚洲国产| 成人免费淫片aa视频免费| 欧美在线一区二区视频| 亚洲精品黄网在线观看| 亚洲第一av网站| 欧美精品日韩三级| 国产欧美精品一区二区三区-老狼| 亚洲成av人乱码色午夜| 91视频国产精品| 国产精品久久久久久超碰| 亚洲精品欧美日韩专区| 久久久av免费| 国产欧美最新羞羞视频在线观看| 亚洲国产高潮在线观看| 亚洲欧洲一区二区三区在线观看| 日韩免费在线看| 亚洲精品一区二区在线| 亚洲第一精品夜夜躁人人爽| 日本欧美一二三区| 亚洲天堂第一页| 欧美丝袜美女中出在线| 成人高清视频观看www| 久久久免费在线观看| 97在线免费观看视频| 亚洲欧美日韩视频一区| 欧美一区二区视频97| 日韩激情av在线免费观看| 北条麻妃一区二区三区中文字幕| 欧美性猛交xxxx免费看| 日韩免费视频在线观看| 精品久久久一区二区| 69视频在线免费观看| 久久九九全国免费精品观看| 色综合亚洲精品激情狠狠| 亚洲xxx大片| 亚洲xxxxx| 日韩电影中文字幕在线| 91精品91久久久久久| 97精品国产97久久久久久免费| 国产乱肥老妇国产一区二| 中文字幕在线观看亚洲| 国产91精品青草社区| 精品一区二区亚洲| 九九精品在线播放| 日韩免费中文字幕| 国产精品一区久久| 91精品视频大全| 2019最新中文字幕| 欧美性xxxx极品hd欧美风情| 亚洲性夜色噜噜噜7777| 日韩欧美高清在线视频| 久久免费观看视频| 欧美视频二区36p| 日韩最新av在线| 国产午夜精品理论片a级探花| 亚洲一区二区久久久| 国产视频精品一区二区三区| 麻豆国产精品va在线观看不卡| 欧美网站在线观看| 久久久久久久久久久91| 成人情趣片在线观看免费| 久久成人在线视频| 国产精品看片资源| 欧美精品videos另类日本| 亚洲欧美在线播放| 久久久精品日本| 亚洲丁香婷深爱综合| 97精品国产91久久久久久| 欧美日韩国产第一页| 欧美一级视频一区二区| 97国产一区二区精品久久呦| 国产精品第一视频| 欧美影院成年免费版| 精品亚洲一区二区| 少妇av一区二区三区| 国产精品网站大全| 欧美天天综合色影久久精品| 久久亚洲精品一区二区| 久久久视频精品| 国产成人精品综合| 欧美日韩在线视频观看| 精品在线欧美视频| 97婷婷涩涩精品一区| 国产精品视频色| 97视频在线播放| 亚洲一区精品电影| 国产精品视频免费在线观看| 欧美一乱一性一交一视频| 一区二区三区国产视频| 亚洲国产美女久久久久| 日韩精品电影网| 国产精品久久久久久久久久新婚| 日韩一区二区三区国产| 久久久久久久久久婷婷| 久久精品国产电影| 97国产在线观看| 成人国内精品久久久久一区| 成人a在线视频| 欧美日韩中文字幕在线视频| 日日狠狠久久偷偷四色综合免费| 亚洲精品720p| 午夜精品久久久久久久男人的天堂|