欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長(zhǎng)資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          探索PHP 生命周期

          探索PHP 生命周期

          學(xué)習(xí) PHP 生命周期

          PHP的生命周期是一個(gè)很復(fù)雜的過(guò)程,其生命周期應(yīng)該被熱衷于使用它的人所掌握。主要內(nèi)容如下:

          PHP 啟動(dòng)。如果運(yùn)行的是 CLI 或者 FPM,它將運(yùn)行 C main()。如果作為模塊運(yùn)行到網(wǎng)絡(luò)服務(wù)器,像使用 apxs2 SAPI (Apache 2),則 PHP 在 Apache 啟動(dòng)后不久啟動(dòng),并開始運(yùn)行其模塊的啟動(dòng)序列,PHP 就是其中之一。在內(nèi)部稱啟動(dòng)為模塊啟動(dòng)步驟。我們也將其縮寫為MINIT步驟。

          一旦啟動(dòng),PHP 將等待處理一個(gè)/幾個(gè)請(qǐng)求。當(dāng)我們談?wù)?PHP CLI時(shí),將只有一個(gè)請(qǐng)求:當(dāng)前腳本要運(yùn)行。但是,當(dāng)我們談?wù)?Web 環(huán)境時(shí)——應(yīng)該是 PHP-FPM 或 Web 服務(wù)器模塊——PHP 可以一個(gè)接一個(gè)地處理多個(gè)請(qǐng)求。這完全依賴于你如何配置你的 Web 服務(wù)器:你可以告訴它處理無(wú)限數(shù)量的請(qǐng)求,或在關(guān)閉并回收該過(guò)程之前處理特定數(shù)量的請(qǐng)求。每次一個(gè)新的請(qǐng)求在線程中要處理時(shí),PHP 就會(huì)運(yùn)行請(qǐng)求啟動(dòng)步驟。我們稱之為 RINIT

          相關(guān)學(xué)習(xí)推薦:PHP編程從入門到精通

          請(qǐng)求得到處理,(可能)生成了一些內(nèi)容,OK。是時(shí)候關(guān)閉請(qǐng)求,并準(zhǔn)備好處理另一個(gè)請(qǐng)求。關(guān)閉請(qǐng)求調(diào)用請(qǐng)求關(guān)閉步驟。我們稱之為RSHUTDOWN?!?/p>

          當(dāng)處理完X個(gè)請(qǐng)求(一個(gè),幾十個(gè),數(shù)千個(gè)等),PHP 最后會(huì)自行關(guān)閉,然后結(jié)束。關(guān)閉 PHP 進(jìn)程稱為模塊關(guān)閉步驟??s寫為 MSHUTDOWN。

          如果我們可以畫出這些步驟,則可能會(huì)得到以下信息:

          探索PHP 生命周期

          并行模型

          在 CLI 環(huán)境,任何事都很容易:一個(gè)進(jìn)程處理一個(gè)請(qǐng)求:它會(huì)啟動(dòng)一個(gè)單獨(dú)的 PHP 腳本,然后結(jié)束。CLI 環(huán)境是 Web 環(huán)境的一種特殊化,它更為復(fù)雜。

          為了同時(shí)處理多個(gè)請(qǐng)求,你必須運(yùn)行并行模型。在 PHP 中存在兩種:

          • The process-based model 基于進(jìn)程的模型
          • The thread-based model 基于線程的模型

          使用基于進(jìn)程的模型,操作系統(tǒng)將每個(gè) PHP 解釋器隔離到自己的進(jìn)程中。這種模型在 Unix 非常普遍。每個(gè)請(qǐng)求都到它自己的進(jìn)程。PHP-CLI、PHP-FPM 和 PHP-CGI 使用該模型。

          在基于線程的模型中,每個(gè) PHP 解釋器都使用線程庫(kù)隔離到線程中。這個(gè)模型主要用在 Windows 操作系統(tǒng),但也可以用在大多數(shù)的 Unix中。要求 PHP 和其擴(kuò)展在 ZTS 模式下被構(gòu)建。

          這是基于進(jìn)程的模型:

          探索PHP 生命周期

          這是基于線程的模型:

          探索PHP 生命周期

          注意

          作為擴(kuò)展開發(fā)者,PHP 的多進(jìn)程模塊不是你的選擇。你將需要支持它。你必須讓你的擴(kuò)展支持在線程環(huán)境中運(yùn)行,特別是在 Windows平臺(tái)下,并且必須針對(duì)它編程。

          PHP 擴(kuò)展鉤子

          你可能猜到了,PHP 引擎將在多個(gè)生命周期點(diǎn)觸發(fā)你的擴(kuò)展。我們稱它們?yōu)?em>鉤子函數(shù)。你的擴(kuò)展程序可以在向引擎注冊(cè)時(shí),通過(guò)聲明函數(shù)鉤子來(lái)聲明對(duì)特定生命周期點(diǎn)的興趣。
          在你分析 PHP 擴(kuò)展結(jié)構(gòu)時(shí)(zend_module_entry 結(jié)構(gòu)),這些鉤子可以清晰地看到:

          struct _zend_module_entry {         unsigned short size;         unsigned int zend_api;         unsigned char zend_debug;         unsigned char zts;         const struct _zend_ini_entry *ini_entry;         const struct _zend_module_dep *deps;         const char *name;         const struct _zend_function_entry *functions;         int (*module_startup_func)(INIT_FUNC_ARGS);        /* MINIT() */         int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);   /* MSHUTDOWN() */         int (*request_startup_func)(INIT_FUNC_ARGS);       /* RINIT() */         int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);  /* RSHUTDOWN() */         void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);     /* PHPINFO() */         const char *version;         size_t globals_size; #ifdef ZTS         ts_rsrc_id* globals_id_ptr; #else         void* globals_ptr; #endif         void (*globals_ctor)(void *global);                /* GINIT() */         void (*globals_dtor)(void *global);                /* GSHUTDOWN */         int (*post_deactivate_func)(void);                 /* PRSHUTDOWN() */         int module_started;         unsigned char type;         void *handle;         int module_number;         const char *build_id; };

          現(xiàn)在讓我們看看你應(yīng)該在這些鉤子中編寫哪種代碼。

          模塊初始化:MINIT()

          這是 PHP 進(jìn)程啟動(dòng)步驟。在擴(kuò)展的MINIT()中,你將加載并分配以后每次請(qǐng)求需要的任何持久對(duì)象或者信息。它們的大部分將分配為只讀對(duì)象。

          MINIT()中,尚未有線程或進(jìn)程彈出,所以你完全可以訪問(wèn)全局變量,而沒(méi)有任何保護(hù)。另外,由于請(qǐng)求尚未啟動(dòng),因此你不能分配請(qǐng)求綁定的內(nèi)存。你永遠(yuǎn)不會(huì)在MINIT()步驟中使用Zend 內(nèi)存管理 分配,但是會(huì)使用永久分配。不是 emalloc(),而是pemalloc()。否則會(huì)導(dǎo)致崩潰。

          MINIT()中,執(zhí)行引擎仍未啟動(dòng),所以不要在沒(méi)有特別注意的情況下,嘗試訪問(wèn)其任何結(jié)構(gòu)。

          如果你需要為你的擴(kuò)展注冊(cè) INI 入口,則MINIT() 是正確的做法。

          如果你要為以后使用而注冊(cè)只讀zend_strings,請(qǐng)使用持久分配了。

          如果你需要分配的對(duì)象在處理請(qǐng)求時(shí)會(huì)寫入,那么你必須復(fù)制它們的內(nèi)存分配到該請(qǐng)求的線程專用池。記住,你只可以在MINIT()中安全地寫入全局空間。

          注意

          內(nèi)存管理、分配和調(diào)試是內(nèi)存管理的部分章節(jié)。

          在 php_module_startup()函數(shù)中,通過(guò)zend_startup_modules()觸發(fā) MINIT()。

          模塊終止:MSHUTDOWN()

          這是 PHP 進(jìn)程終止步驟。很容易, 基本上,你在這里運(yùn)行了與MINIT()中使用的相反的操作。你釋放了資源,取消 INI 設(shè)置的注冊(cè)等等。

          再次注意:執(zhí)行引擎是關(guān)閉的,所以你不應(yīng)在此處訪問(wèn)其任何變量。

          由于你在此處不需要請(qǐng)求,所以不應(yīng)使用Zend 內(nèi)存管理 的efree() 或類似函數(shù)去釋放資源,但對(duì)于釋放持久分配,使用pefree()

          在php_module_shutdown()函數(shù)中,由zend_shutdown()zend_destroy_modules()中觸發(fā)MSHUTDOWN()。

          請(qǐng)求初始化: RINIT()

          剛剛看過(guò)的請(qǐng)求,PHP 將在這里處理它。在RINIT()中,你引導(dǎo)了處理該精確請(qǐng)求所需的資源。PHP 是一種無(wú)共享架構(gòu),它提供了內(nèi)存管理功能。

          RINIT()中,如果需要分配動(dòng)態(tài)內(nèi)存,你將使用Zend 內(nèi)存管理器。你將調(diào)用 emalloc()。Zend 內(nèi)存管理器 追蹤你通過(guò)它分配的內(nèi)存,當(dāng)請(qǐng)求關(guān)閉時(shí),如果你忘記這么做,它將嘗試釋放請(qǐng)求綁定的內(nèi)存(你不應(yīng)這么做)。

          在這里,你不應(yīng)請(qǐng)求持久的動(dòng)態(tài)內(nèi)存,即 libc 的malloc() 或Zend 的pemalloc()。如果你在這里請(qǐng)求持久內(nèi)存,并且忘記釋放它,則將造成泄露,并且隨著 PHP 處理越來(lái)越多的請(qǐng)求而堆積,最終導(dǎo)致進(jìn)程崩潰(Kernel OOM) ,并且導(dǎo)致機(jī)器內(nèi)存不足。

          另外,務(wù)必注意不要在這里寫入全局空間。如果 PHP 作為選定的并行模型運(yùn)行到線程中,那么你將修改每個(gè)線程池中的上下文(所有與你的請(qǐng)求并行處理的請(qǐng)求),并且如果你沒(méi)有鎖定內(nèi)存,也可能觸發(fā)競(jìng)爭(zhēng)條件。如果你需要全局,你必須保護(hù)它們。

          注意

          全局范圍管理解釋在專用章節(jié)。

          在php_request_startup()函數(shù)中,通過(guò)zend_activate_module()觸發(fā)RINIT()。

          請(qǐng)求終止: RSHUTDOWN()

          這是 PHP 請(qǐng)求終止步驟。PHP 剛結(jié)束處理其請(qǐng)求,現(xiàn)在來(lái)清理其部分作為無(wú)共享架構(gòu)的內(nèi)存。接下來(lái)的請(qǐng)求不應(yīng)記住當(dāng)前請(qǐng)求的任何內(nèi)容。很容易,基本上,你在此處執(zhí)行了與RINIT()使用的相反的操作。你釋放了請(qǐng)求綁定的資源。

          由于你在此處使用了請(qǐng)求,你應(yīng)使用 Zend 內(nèi)存管理器的efree()或類似方式釋放資源。如果你忘記釋放并且造成泄露,在調(diào)試版本下,內(nèi)存管理器將在進(jìn)程stderr上記錄關(guān)于泄露的指針的日記,并且將為你釋放它們。

          給你個(gè)主意,RSHUTDOWN()將被調(diào)用:

          • 執(zhí)行用戶區(qū)關(guān)閉功能后 (register_shutdown_function())
          • 在調(diào)用每個(gè)對(duì)象析構(gòu)函數(shù)之后
          • PHP 輸出緩沖區(qū)刷新之后
          • 禁用 max_execution_time 之后

          在php_request_shutdown()函數(shù)中,通過(guò)zend_deactivate_modules()觸發(fā)RSHUTDOWN()。

          Post 請(qǐng)求終止: PRSHUTDOWN()

          這個(gè)鉤子很少使用。它在 RSHUTDOWN()之后調(diào)用,但是中間還會(huì)運(yùn)行一些額外的引擎代碼。
          尤其是在 Post-RSHUTDOWN 中:

          • PHP 輸出緩沖區(qū)已關(guān)閉,并且它的處理程序已刷新
          • PHP 超全局已經(jīng)銷毀
          • 執(zhí)行引擎已經(jīng)關(guān)閉

          這個(gè)鉤子很少使用。在php_request_shutdown()函數(shù)中,通過(guò)zend_post_deactivate_modules(),在RSHUTDOWN()之后被觸發(fā)。

          全局初始化: GINIT()

          線程庫(kù)每次彈出線程時(shí)都會(huì)調(diào)用該鉤子。如果你使用多進(jìn)程,當(dāng) PHP 啟動(dòng),僅在觸發(fā) MINIT() 之前調(diào)用此函數(shù)。

          這里不講太多細(xì)節(jié),只需在這里簡(jiǎn)單地初始化全局變量,通常初始化為0。全局管理將在專用章節(jié)詳細(xì)說(shuō)明。

          記住,全局變量不會(huì)在每次請(qǐng)求后清理。如果你需要為每次新的請(qǐng)求重置它們(可能),那么你必須將這樣地進(jìn)程放到RINIT()中。

          注意

          全局范圍管理在專用章節(jié)詳細(xì)介紹。

          全局終止: GSHUTDOWN()

          在線程庫(kù)中,每當(dāng)線程終止時(shí)都會(huì)調(diào)用該鉤子。如果你使用多線程,該函數(shù)將在 PHP 終止期間(在MSHUTDOWN())被調(diào)用一次。

          在這里不提供太多細(xì)節(jié),你只需簡(jiǎn)單地在這里取消初始化你的全局變量,通常你不必做什么,但如果在構(gòu)建全局(GINIT())時(shí)分配了資源,在這里的步驟你應(yīng)該釋放它們。

          全局管理將在專用章節(jié)詳細(xì)介紹。

          記住,全局變量在每次請(qǐng)求后不會(huì)清除。即GSHUTDOWN()不會(huì)作為RSHUTDOWN()的一部分被調(diào)用。

          注意

          全局范圍管理在專用章節(jié)有詳細(xì)介紹。

          信息收集: MINFO()

          該鉤子很特殊,它永遠(yuǎn)不會(huì)被引擎自動(dòng)觸發(fā),只有你詢問(wèn)它有關(guān)擴(kuò)展的信息時(shí)才會(huì)觸發(fā)。典型的例子是調(diào)用phpinfo()。然后運(yùn)行此函數(shù),并將有關(guān)當(dāng)前擴(kuò)展的特殊信息打印到流中。

          簡(jiǎn)而言之,phpinfo() 展示信息。

          該函數(shù)也可以通過(guò) CLI 使用反射開關(guān)之一調(diào)用,例如php --ri pib 或通過(guò)用戶區(qū)調(diào)用ini_get_all()

          你可以將其留空,在這種情況下,只有擴(kuò)展的名字顯示,沒(méi)有其他(可能不會(huì)顯示 INI 設(shè)置,因?yàn)檫@是 MINFO() 的一部分)。

          關(guān)于 PHP 生命周期的思考

          探索PHP 生命周期

          你可能已經(jīng)發(fā)現(xiàn)了,RINIT()RSHUTDOWN() 尤其重要,因?yàn)樗鼈冊(cè)跀U(kuò)展中被觸發(fā)成千上萬(wàn)次。如果 PHP 步驟是關(guān)于 Web (不是 CLI),并且已經(jīng)配置為可以處理無(wú)數(shù)次請(qǐng)求,那么你的 RINIT()/RSHUTDOWN() 組將被無(wú)數(shù)次調(diào)用。

          我們想要再次引起你對(duì)內(nèi)存管理的關(guān)注。在處理請(qǐng)求時(shí)(在RINIT()RSHUTDOWN()之間),你最終泄露的小字節(jié),將對(duì)滿載服務(wù)器產(chǎn)生嚴(yán)重影響。這就是為什么建議你使用 Zend 內(nèi)存管理器 進(jìn)行此類分配,并且準(zhǔn)備好調(diào)試內(nèi)存布局。作為無(wú)共享架構(gòu)的一部分,PHP 在每次請(qǐng)求最后都會(huì)忘記并釋放請(qǐng)求內(nèi)存,這是 PHP 的內(nèi)部設(shè)計(jì)。

          另外,如果你的崩潰信號(hào)是 SIGSEGV (壞內(nèi)存訪問(wèn)),則整個(gè)進(jìn)程會(huì)崩潰。如果 PHP 是使用線程作為多進(jìn)程引擎,那么你所有其他線程也將崩潰,甚至可能造成服務(wù)器崩潰。

          注意

          C 語(yǔ)言不是 PHP 語(yǔ)言。使用 C,在程序的錯(cuò)誤很可能導(dǎo)致程序的崩潰與終止。

          通過(guò)重寫函數(shù)指針進(jìn)行掛鉤

          現(xiàn)在你知道引擎何時(shí)會(huì)觸發(fā)代碼,還存在值得注意的函數(shù)指針,你可以替換它們來(lái)掛載到引擎。因?yàn)槟切┲羔樖侨肿兞?,因此你可以將它們替換為 MINIT() 步驟,并將它們放回MSHUTDOWN()中。

          感興趣的有:

          • AST, Zend/zend_ast.h:

            • void (zend_ast_process_t)(zend_ast ast)
          • Compiler, Zend/zend_compile.h:

            • zend_op_array (zend_compile_file)(zend_file_handle file_handle, int type)*
            • zend_op_array (zend_compile_string)(zval source_string, char filename)
          • Executor, Zend/zend_execute.h:

            • void (zend_execute_ex)(zend_execute_data execute_data)
            • void (zend_execute_internal)(zend_execute_data execute_data, zval return_value)*
          • GC, Zend/zend_gc.h:

            • int (gc_collect_cycles)(void)*
          • TSRM, TSRM/TSRM.h:

            • void (tsrm_thread_begin_func_t)(THREAD_T thread_id)*
            • void (tsrm_thread_end_func_t)(THREAD_T thread_id)*
          • Error, Zend/zend.h:

            • void (zend_error_cb)(int type, const char error_filename, const uint error_lineno, const char format, va_list args)*
          • Exceptions, Zend/zend_exceptions.h:

            • void (zend_throw_exception_hook)(zval ex)
          • Lifetime, Zend/zend.h:

            • void (zend_on_timeout)(int seconds)*
            • void (zend_interrupt_function)(zend_execute_data execute_data)
            • void (zend_ticks_function)(int ticks)*

          還有其他存在,但是上面的是最重要的,當(dāng)你設(shè)計(jì) PHP 擴(kuò)展時(shí),你可能需要。因?yàn)樗鼈兊拿趾苋菀卓?,所以不再詳?xì)解釋它們。

          如果你需要

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)