深刻剖析PHP7內核源碼(一)- PHP架構與生命週期

PHP7 爲何這麼快?

  • 全新的zval 更節約的空間,棧上分配內存
  • zend_string 存儲字符串的Hash值,數組查詢的時候不須要進行Hash計算
  • 在HashTable桶內直接存數據,減小了內存的申請次數,提高了cache命中率和內存訪問速度
  • zend_parse_parameters改成了宏實現,性能提高5%
  • 增長opcode指令 call_user_function,is_init/string/array,strlen,defined函數變成opcode指令,速度更快
  • 排序算法的改進

PHP7 架構

  • Zend 引擎:Zend引擎爲PHP提供了基礎服務,包括詞法分析 語法分析 ,AST抽象語法樹編譯 opcodes執行,PHP的變量設計、內存管理、進程管理。
  • PHP層:綁定了SAPI層並處理與它的通訊,它同時對safe_mode和open_basedir的檢測提供一致的控制層,將fopen()、fread()和fwrite()等用戶空間的函數與文件和網絡I/O聯繫起來。
  • SAPI:包括了cli fpm等,把接口對外接口都抽象出來,只要遵照SAPI協議即可以實現一個server。
  • 拓展:zend 引擎提供了核心能力和接口規範,在此基礎上能夠開發拓展

這裏的拓展分爲了兩種,一般在php.ini中,經過extension=加載的擴展咱們稱爲PHP擴展,經過zend_extension=加載的擴展咱們稱爲Zend擴展,但從源碼的角度來說,PHP擴展應該稱爲「模塊」(源碼中以module命名),而Zend擴展稱爲「擴展」(源碼中以extension命名)。二者最大的區別在於向引擎註冊的鉤子,向用戶層面提供一些C實現的PHP函數,須要用到zend_module_entry(即做爲PHP擴展),而須要hook到Zend引擎的話,就得用到zend_extension(即做爲Zend擴展)。php

PHP7執行流程

  1. 詞法分析,把源代碼切割成多個字符串單元(Token)
  2. 語法分析器把Token轉換成AST抽象語法樹
  3. 抽象語法樹轉換成opcodes(opcode指令集合)
  4. 虛擬機解釋執行執行opcodes(opcode是一組指令標識,對應handler處理函數)

執行實例

詞法分析
<?php 
echo "Hello world";

切割成了4部分html

<?php  => #define T_OPEN_TAG 379

echo => #define T_ECHO 328

空格 =>  #define T_WHITESPACE 382

"hello world" => #define T_CONSTANT_ENCAPSED_STRING 323
語法分析

單獨存在的詞塊不能完整表達語義,還須要語法分析器,它會檢查語法,匹配Token,對Token進行關聯,組織串聯後的產物就是AST.AST 分爲多種類型,對應PHP語法,好比賦值語句,生成的抽象語法樹節點是ZEND_AST_ASSIGN,賦值語句的左右會被做爲ZEND_AST_ASSIGN類型節點的孩子(AST是PHP7才加入的,解耦了編譯器和解釋器).git

opcodes

opcode是PHP執行過程當中的中間代碼,生成後由虛擬機執行,生成的opcode是相似下面的樣子github

line     op
1        ECHO
2        RETURN

源碼中對應的opcode及handler算法

ZEND_ECHO // handler:ZEND_ECHO_SPEC_CONST_HANDLER 實現的功能是輸出"hello world"
ZEND_RETURN  // handler:ZEND_RETURN_SPEC_CONST_HANDLER

PHP 生命週期

CLI生命週期

  • php_module_startup:註冊全局變量GPC等,加載內部拓展和外部拓展。
  • php_request_startup:重置垃圾回收器,初始化執行器,初始化掃描器,設置超時時間等。
  • php_execute_script
=> compile_file
=> open_file_for_scanning(讀取PHP代碼內容,並使詞法分析指針指向第一個位置) 
=> zendparse(詞法分析語法分析後生成AST) => init_op_array(初始化op_array) 
=> zend_compile_top_stmt(把AST轉爲op_array) 
=> pass_two(設置op_array對應的zend虛擬機handler) 
=> 生成op_array
=> zend_execute(zend虛擬機中執行op_array)
  • php_request_shutdown:調用全部關閉函數,調用全部析構函數,輸出緩衝區內容,重置最大執行時間,關閉輸出層(HTTP頭等),釋放全部request的全局變量
  • php_module_shutdown:調用module對應的flush函數,清理持久化的符號表,銷燬全局變量,關閉全部拓展,關閉內存管理,關閉輸出output,析構垃圾回收

FPM模式的生命週期

  • FPM跟CLI模式不一樣的是,FPM是常駐內存的,因此php_module_startup只在啓動進程的時候作一次初始化,對應的php_module_shutdown也只作一次。
  • 進入循環,調用fcgi_accept_request(accept) 阻塞等待,若是請求進來,則進入php_request_startup,初始化請求,同時加了鎖來防止驚羣效應
fcgi.c 
...
FCGI_LOCK(req->listen_socket); 
req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
FCGI_UNLOCK(req->listen_socket);

引用數組

相關文章
相關標籤/搜索