PHP7新特性、運行模式和生命週期

1、PHP目錄

    PHP源碼核心目錄 Zend文件下,spai放在sapi的文件下,一些擴展的方法和庫函數在ext文件下

    編譯的腳本是在configure目錄下,接下來make進行編譯,make install 會把相關的二進制文件拷貝到我指定的目錄下

 ./configure  --prefix=/usr/tmp/php/php-7.1.0  --enable-fpm --enable-debug

 2、PHP7新特性:

  太空船操作符:用於比較2個表達式,例如當a小於,等於或大於a小於,等於或大於b時,分別返回-1,0,1

 echo 1 <=> 1; //0 echo PHP_EOL; echo 1 <=> 2; //-1 echo PHP_EOL; echo 2 <=> 1; //1

  類型聲名:declare(strict_types=1);//strict_types=1表示開啓嚴格模式

declare(strict_types=1); //strict_types=1表示嚴格模式
function sum(int ...$ints){
    return array_sum($ints);
}
var_dump(sum(1,'2','3.1',4.1));

執行結果
Fatal error: Uncaught TypeError: Argument 2 passed to sum() must be of the type integer, string given

  null合併操作符: $page=isset($_GET['page'])?$_GET['page']:0;  等於 $page=$_GET['page']??0;

 常量數組: define('ANIMALS',['dog','cat']);

 批量導入: use Space\{Class A,ClassB,Class as C};

 throwable接口

try{
  undefindfunc();
}catch(Error $e){
var_dump($e);
}
//可以捕獲沒有定義函數的這個錯誤

 Clousure:call()   

//在php7可通過call來暫時綁定一個閉包對象到$this對象並調用它
class Test{
 private $num=1;
}
$f=function(){
 return $this->num+1;
}
echo $f->call(new Test);

//在php7之前,當動態的給一個對象添加方法時,可以通過Closure來複制一個閉包對象,並綁定到一個$this對象和類作用域

class People{
    private $age=10;
}
$f=function(){
    return $this->age+1;
};

$p=$f->bindTo(new People,'People');
echo $p();

intdiv函數: intdiv(10,3) //10除以三取整數

list的方括號寫法:list($a,$b,$c)=$arr[1,2,31]  等於 [$a,$b,$c]=$arr;

PHP運行原理

1、目前常見的4種PHP運行模式

  1. CGI通用網關接口模式
  2. FAST-CGI模式
  3. CLI命令行模式
  4. 模塊模式

CGI通用網關接口模式

   CGI就是將Web服務器和PHP執行程序連接起來,把接受指令傳遞給PHP執行程序,每有一個用戶請求,都會先要創建CGI6的子進程,然後處理請求,用戶請求數量非常多會大量擠佔系統的資源,造成效率低下,所以有多長連接請求就有多少CGI子進程,子進程反覆加載時導致CGI性能低下的主要原因

FAST-CGI模式

是cgi的升級版本,FastCGI 像是一個常駐 (long-live) 型的 CGI,它可以一直執行着,只要激活後,不會每次都要花費時間去fork 一次,也是一種協議

FastCGI的工作原理是:

  (1)、Web Server啓動時載入FastCGI進程管理器【PHP的FastCGI進程管理器是PHP-FPM(php-FastCGI Process Manager)】(IIS ISAPI或Apache Module);

  (2)、FastCGI進程管理器自身初始化,啓動多個CGI解釋器進程 (在任務管理器中可見多個php-cgi.exe)並等待來自Web Server的連接。

 (3)、當客戶端請求到達Web Server時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server將CGI環境變量和標準輸入發送到FastCGI子進程php-cgi。

  (4)、FastCGI子進程完成處理後將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關閉連接時,請求便告處理完成。FastCGI子進程接着等待並處理來自FastCGI進程管理器(運行在 WebServer中)的下一個連接。在正常的CGI模式中,php-cgi.exe在此便退出了。

  在CGI模式中,可以想象 CGI通常有多慢。每一個Web請求PHP都必須重新解析php.ini、重新載入全部dll擴展並重初始化全部數據結構。使用FastCGI,所有這些都只在進程啓動時發生一次。一個額外的好處是,持續數據庫連接(Persistent database connection)可以工作。

CLI命令行模式

一般使用調用腳本、查看php信息時會使用到該模式

php -r"phpinfo();" |less 分頁顯示

 

模塊模式

  1. Apache + mod_php
  2. lighttp + spawn-fcgi
  3. nginx + PHP-FPM(php在Nginx中運行模式)

運行原理

PHP-CGI:fast-cgi是一種協議,而php-cgi是實現了這種協議的進程。不過這種實現比較爛。它是單進程的,一個進程處理一個請求,處理結束後進程就銷燬

PHP - FPM:是對php-cgi的改進版,它直接管理多個php-cgi進程/線程。也就是說,php-fpm是php-cgi的進程管理器因此它也算是fastcgi協議的實現
php的運行原理,就是在服務器啓動時,自動載入PHP-FPM進程管理器,從而管理多個PHP-CGI進程來準備響應用戶的請求,如下圖所示:

由於php-cgi是隨服務器啓動載入的,所以初始化變量只會發生一次

運行模式和運行原理的區別

多個運行模式相當於超市的不同入口,運行原理就是進入超市後的固定的行走路線,通過不同的運行模式進入到底層(進入超市)

SAPI簡介

 SAPI相當於PHP外部環境的代理器。PHP可以應用在終端上(CLI SAPI),也可以應用在Web服務器上(CGI SAPI)

 

 

PHP運行的生命週期

  在模塊初始化前,首先調用sapi_startup(sapi_module),對sapi_model進行初始化工作,通過調用sapi_model的startup函數(模塊啓動調用的函數),CLI調用php_cli_startup函數,該函數又調用了php_module_startup函數,也就是對應的模塊初始化

   CLI模式生命週期:

     php_module_startup  模塊初始化階段(註冊內部擴展、加載外部擴展、啓動附加的PHP擴展、啓動各個模塊、禁用php.ini裏面的禁用函數和類)

     php_request_startup  請求初始化階段 (初始化輸出handler的棧,並把OG(FLAGS置爲使用)、調用zend_activate(初始化GC、初始化編譯器、初始化執行器、初始化掃描器)、對信號進行處理、設置超時時間、初始化相關全局變量)

     php_execute_script    腳本執行階段(讀取的PHP代碼進行解析,先詞法解析並使詞法分析指針指向第一個位置,解析成token,然後語法解析,生成抽象語法樹,然後通過對抽象語法樹進行遍歷生成opcode,opcode在虛擬機上執行得到對應的結果)

    php_request_shutdown 請求關閉階段(調用各個模塊中註冊的關閉函數和析構函數、將輸出緩衝器重內容輸出、調用所有擴展註冊的鉤子rshutdown函數、銷燬request相關的全局變量、關閉編譯器和執行器、還原ini配置)(完成這些工作,fpm模式會循環等待請求到來)

    php_module_shutdown 模塊關閉階段(調用sapi_flush() 然後進行銷燬所有模塊、全局變量、關閉擴展、銷燬ini對應的HashTable、關閉ini config、關閉內存管理器、關閉輸出output、析構垃圾回收)

FPM

  FPM是一個FastCGI進程管理器,對於5.3之前只是一個補丁包,從5.3開始,PHP集成了PHP-FPM。提供了更好的PHP的進程管理方式,可以有效控制內存和進程,支持平滑重啓PHP和重載PHP配置

FPM的三種模式

  pm=static  始終保證一個固定的子進程數,子進程數一般由pm.max_children = 20來指定的,起20個work進程

  pm=dynamic 動態模式,啓動的時候生成固定數量子進程,由pm.start_servers = 10來控制,也就是最小的子進程數,最大是                           由pm.max_children 控制,另外還支持兩種參數min_spare_servers = 10和max_spare_servers = 20最大和最小閒                         置進程數的最小值和最大數目

  pm=ondemand 按需要的一個模式,和動態模式相反,是按內存放在第一位的,每個閒置進程都會在一定時間內被殺死,這個                          模式的好處是低峯期的話,內存會降下來,弊端就是高峯期頻繁創建進程的弊端

網絡編程:

 socket   首先通過socket創建一個fd

 bind  設置端口號,使用bind函數進行綁定

 listen  監聽

 accept 一般是進入一個死循環,調用accept函數進行接收請求,如果沒有請求函數的話會掛起,請求進入阻塞狀態,這個時候會把cpu讓出節省資源,如果請求來了,accept函數進行接收,然後讀取請求 處理自己的邏輯接着返回請求

   

信號處理:

  真正處理請求的是work進程不是master進程,master進程只是起到了管理和監聽work進程的作用,當work進程掛掉了會發送信號給master請求,然後一旦請求過來了,master進程會拉起一個work請求進行處理,請求由Nginx的worker進行處理,轉出對應的FastCGI,請求FPM,accept由FPM的worker進程處理,執行完畢再返回給Nginx,Nginx再進一步返回給Client,所以:

  當 kill master的時候,php-fpm進程全部退出,都不能工作

  當kill  -9 master的時候,可以工作

  當kill  work進程的時候,可以工作,會發現拉起一個新的work進程

kill就是給某個進程id發送了一個信號。默認發送的信號是SIGTERM,而kill -9發送的信號是SIGKILL,即exit。exit信號不會被系統阻塞,所以kill -9能順利殺掉進程。

系統會發送一個SIGTERM的信號給對應的程序。當程序接收到該signal後,將會發生以下的事情

  1. 程序立刻停止
  2. 當程序釋放相應資源後再停止
  3. 程序可能仍然繼續運行

      大部分程序接收到SIGTERM信號後,會先釋放自己的資源,然後在停止。但是也有程序可以在接受到信號量後,做一些其他的事情,並且這些事情是可以

      配置的。如果程序正在等待IO,可能就不會立馬做出相應。

  也就是說,SIGTERM多半是會被阻塞的、忽略。

FPM模式的生命週期:

 

 php_module_startup:模塊的初始化

 fcgi_accept_request:對請求的接受,進入循環,實際上調用的是accept,阻塞等待請求,如果有請求進來,會被換起,進入php_request_startup,初始化請求。爲了防止多個進程對accept進行搶佔出現驚羣現象,增加了鎖機制,但是在Linux的2.6內核上,阻塞版accept不存在驚羣現象了

 php_request_startup  請求初始化階段

 php_execute_script    腳本執行階段(讀取的PHP代碼進行解析,先詞法解析,解析成token,然後語法解析,生成抽象語法樹,然後通過對抽象語法樹進行遍歷生成opcode,opcode在虛擬機上執行得到對應的結果)

 fpm_request_end:請求的關閉

  php_request_shutdown 請求關閉階段

  php_module_shutdown 模塊關閉階段

1、因爲Fpm是一個常駐內存的這麼一個進程,它需要請求完一個並接受下一個請求並處理,所以進行一個循環,當FPM整個退出的時候才需要執行 php_module_shutdown

2、其實FCGI_LOCK/FCGI_UNLOCK在Linux已經沒有實現了,因爲在Linux2.6內核上,阻塞版本的accept系統調用不存在驚羣了,當新的連接過來時,大家會發現,僅有一個子進程返回新建的鏈接,其他子進程繼續休眠在accept調用上,沒有驚羣現象

3、 主進程創建了socket、bind、listen之後,fork()出來多個進程,worker進程會進入循環(accept)這個listen_fd,當沒有請求的時候,會阻塞在fcgin_accept_request(accept),讓出cpu資源,成爲空閒進程,當請求到達時會有一個worker進程搶到並處理進入FastCGI的處理階段

4、歷史上,Linux的accpet存在驚羣問題,但現在2.6以後的內核都解決該問題了。即,當多個進程/線程都阻塞在對同一個socket的接受調用上時,當有一個新的連接到來,內核只會喚醒一個進程,其他進程保持休眠,壓根就不會被喚醒。

  主進程創建了socket、bind、listen之後,fork()出來多個進程,每個子進程都開始循環處理(accept)這個listen_fd。每個進程都阻塞在accept上,當一個新的連接到來時候,所有的進程都會被喚醒,但是其中只有一個進程會接受成功,其餘皆失敗,重新休眠。

 

FastCGI協議:

Nginx收到HTTP請求,然後Nginx直接把HTTP協議的包進行解包然後做一次封裝,然後轉發給PHP-FPM,一般PHP-FPM監聽9000端口,Nginx向PHP-FPM發出的請求就是FastCGI編碼的,這裏是一個個的鍵值對,又有頭部和尾部的一個編碼,然後PHP-FPM對FastCGI的協議的一個解碼,然後走到FPM的生命週期裏面,然後轉到index.php或者其他PHP文件裏面,然後進行腳本的執行,執行完以後由合成一個FastCGI協議返送給Nginx,返回的時候比較簡單一些,按照HTTP協議的方式進行內容編碼,但是還是FastCGI協議,Nginx再解析成HTTP協議發送給客戶端

FastCGI協議兩部分:第一部分傳入到Nginx傳入FPM 注入協議

 

總結:

   整個FPM模式實際上是一個多進程模式,首先由calling process進程fork出master進程,然後master進程會創建Socket,然後fork出worker進程,worker進程會在accept處阻塞等待,請求過來時,由其中一個worker進程處理,按照FastCGI模式進行各階段讀取,另外,FPM建立計分板機制,關注全局和每個woker工作情況,方便使用者監控

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章