PHP內核剖析 常見的SAPI及Embed

參考文獻:https://blog.csdn.net/zhuocr/article/details/60328967 、《PHP7內核剖析》秦鵬/著

SAPI:Server Application Programming Interface 服務器端應用編程端口。它就是PHP與其它應用交互的接口,PHP腳本要執行有很多種方式,通過Web服務器,或者直接在命令行下,也可以嵌入在其他程序中。
        SAPI提供了一個和外部通信的接口,常見的SAPI有:cgi 、fast-cgi、cli、isapi、apache 模塊的 DLL

1、常見的SAPI介紹

CGI

CGI即通用網關接口(Common Gateway Interface),一段把網頁和web服務器中的執行程序連接起來的程序。它把HTML接收的指令傳遞給服務器執行程序,再把服務器執行程序的結果返還給HTML頁。CGI的跨平臺性也極強,幾乎能在任何操作系統上運行。

CGI在遇到用戶連接請求時要先創建CGI的子進程,然後處理請求,處理完後結束這個子進程。這就是fork-and-execute模式。不過有多少連接請求就有多少CGI子進程,會造成資源的擁擠。

fast-cgi

CGI的升級版本,常住型的CGI,前面介紹過。PHP的Fpm就是FastCGI Process Manager,全稱爲PHP FastCGI進程管理器。FastCGI的工作原理:

(1)Web Server啓動時載入FastCGI進程管理器(IIS ISAPI或Apache Module)
            (2)FastCGI進程管理器自身初始化,啓動多個CGI解釋器進程(可見多個php-cgi)並等待來自Web Server的連接。
            (3)當客戶端請求到達Web Server時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server將CGI環境變量和標準輸入發送到FastCGI子進程php-cgi。
             (4)FastCGI子進程完成處理後將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關閉連接時,請求便告處理完成。FastCGI子進程接着等待並處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出了。

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

APACHE2HANDLER
           PHP作爲Apache模塊,Apache服務器在系統啓動後,預先生成多個進程副本駐留在內存中,一旦有請求出現,就立即使用這些空餘的子進程進行處理,這樣就不存在生成子進程造成的延遲了。這些服務器副本在處理完一次HTTP請求之後並不立即退出,而是停留在計 算機中等待下次請求。對於客戶瀏覽器的請求反應更快,性能較高。


           apache模塊的DLL:
           該運行模式是我們以前在windows環境下使用apache服務器經常使用的,而在模塊化(DLL)中,PHP是與Web服務器一起啓動並運行的。(是apache在CGI的基礎上進行的一種擴展,加快PHP的運行效率)

 ISAPI: 
            ISAPI即Internet Server Application Program Interface,是微軟提供的一套面向Internet服務的API接口.一個ISAPI的DLL,可以在被用戶請求激活後長駐內存,等待用戶的另一個請求,還可以在一個DLL裏設置多個用戶請求處理函數,此外,ISAPI的DLL應用程序和WWW服務器處於同一個進程中,效率要顯著高於CGI。

cli:
           cli是php的命令行運行模式,大家經常會使用它,但是可能並沒有注意到(例如:我們在linux下經常使用 “php -m”查找PHP安裝了那些擴展就是PHP命令行運行模式;

在windows下使用cli模式從命令行進入www目錄,然後運行。

Embed

如果我們自己的第三方程序想使用PHP,那麼我們就需要這類SAPI,它在編譯後就是普通的庫文件(可以選擇編譯爲靜態庫、共享庫),我們可以在其它C/C++應用中調用PHP提供的API甚至可以提供給其它語言處理

2、Embed

編譯PHP時通過 --enable-embed=[shared|static]指定庫類型,默認是共享庫。編譯之後可以在PHP安裝位置的/lib目錄下看到生成的庫文件,同時在/include/php/sapi目錄下會生成一個存放Embed頭文件的目錄。

Embed的實現邏輯非常簡單,只是把PHP生命週期的幾個處理函數進行了封裝,它對外提供了兩個API。

2.1  php_embed_init()

這個接口主要進行PHP框架的初始化操作,比如TSRM、初始化SAPI、初始化信號處理,另外它還完成了非常重要的兩個操作,那就是php_module_startup()、php_request_startup()。

EMBED_SAPI_API int php_embed_init(int argc, char **argv)
{
	zend_llist global_vars;
#ifdef ZTS
  //初始化SAPI
  tsrm_startup(1, 1, 0, NULL);
  (void)ts_resource(0);
  ZEND_TSRMLS_CACHE_UPDATE();
#endif

	zend_signal_startup();

  //初始化SAPI
  sapi_startup(&php_embed_module);
  ...
  //進入module startup階段
  if (php_embed_module.startup(&php_embed_module)==FAILURE) {
	  return FAILURE;
  }
  ...
  //請求request startup階段
  if (php_request_startup()==FAILURE) {
	  php_module_shutdown();
	  return FAILURE;
  }
  ...
}

在第三方應用中嵌入PHP時首先需要調用這個接口,然後就可以使用PHP/Zend提供的API完成PHP腳本的執行了。

2.2  php_embed_shutdown()

此接口與php_embed_init()對應,主要完成PHP框架的關閉收尾工作,包括request shutdown、module shutdown兩個階段的操作。

EMBED_SAPI_API void php_embed_shutdown(void)
{
    //關閉請求,request shutdown階段
	php_request_shutdown((void *) 0);
    //關閉模塊
	php_module_shutdown();
	sapi_shutdown();

#ifdef ZTS
    tsrm_shutdown();
	TSRMLS_CACHE_RESET();
#endif

	if (php_embed_module.ini_entries) {
		free(php_embed_module.ini_entries);
		php_embed_module.ini_entries = NULL;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章