深刻理解php底层:php生命周期

 一、PHP的运行模式:

    PHP两种运行模式是WEB模式、CLI模式。不管哪一种模式,PHP工做原理都是同样的,做为一种SAPI运行。php

一、当咱们在终端敲入php这个命令的时候,它使用的是CLI。mysql

它就像一个web服务器同样来支持php完成这个请求,请求完成后再从新把控制权交给终端。linux

二、当使用Apache或者别web服务器做为宿主时,当一个请求到来时,PHP会来支持完成这个请求。通常有:web

 

    多进程(一般编译为apache的模块来处理PHP请求)sql

    多线程模式数据库

二、一切的开始: SAPI接口

    一般咱们编写php Web程序都是经过Apache或者Nginx这类Web服务器来测试脚本. 或者在命令行下经过php程序来执行PHP脚本. 执行完成脚本后,服务器应答,浏览器显示应答信息,或者在命令结束后在标准输出显示内容. 咱们不多关心PHP解释器在哪里. 虽然经过Web服务器和命令行程序执行脚本看起来很不同. 实际上她们的工做是同样的. 命令行程序和Web程序相似, 命令行参数传递给要执行的脚本,至关于经过url 请求一个PHP页面. 脚本戳里完成后返回响应结果,只不过命令行响应的结果是显示在终端上. 脚本执行的开始都是经过SAPI接口进行的. apache

        1)、启动apache:当给定的SAPI启动时,例如在对/usr/local/apache/bin/apachectl start的响应中,PHP由初始化其内核子系统开始。在接近启动例程的末尾,它加载每一个扩展的代码并调用其模块初始化例程(MINIT)。这使得每一个扩展能够初始化内部变量、分配资源、注册资源处理器,以及向ZE注册本身的函数,以便于脚本调用这其中的函数时候ZE知道执行哪些代码。编程

       2)、请求处理初始化:接下来,PHP等待SAPI层请求要处理的页面。对于CGI或CLI等SAPI,这将马上发生且只发生一次。对于Apache、IIS或其余成熟的web服务器SAPI,每次远程用户请求页面时都将发生,所以重复不少次,也可能并发。无论请求如何产生,PHP开始于要求ZE创建脚本的运行环境,而后调用每一个扩展的请求初始化 (RINIT)函数。RINIT使得扩展有机会设定特定的环境变量,根据请求分配资源,或者执行其余任务,如审核。 session扩展中有个RINIT做用的典型示例,若是启用了session.auto_start选项,RINIT将自动触发用户空间的session_start()函数以及预组装$_SESSION变量。api

      3)、执行php代码: 一旦请求被初始化了,ZE开始接管控制权,将PHP脚本翻译成符号,最终造成操做码并逐步运行之。如任一操做码须要调用扩展的函数,ZE将会把参数绑定到该函数,而且临时交出控制权直到函数运行结束。浏览器

       4)、脚本结束:脚本运行结束后,PHP调用每一个扩展的请求关闭(RSHUTDOWN)函数以执行最后的清理工做(如将session变量存入磁盘)。接下来,ZE执行清理过程(垃圾收集)-有效地对以前的请求期间用到的每一个变量执行unset()。

       5)、sapi关闭:一旦完成,PHP继续等待SAPI的其余文档请求或者是关闭信号。对于CGI和CLI等SAPI,没有“下一个请求”,因此SAPI马上开始关闭。关闭期间,PHP再次遍历每一个扩展,调用其模块关闭(MSHUTDOWN)函数,并最终关闭本身的内核子系统。

       简要的过程以下:

      1. PHP是随着Apache的启动而运行的;
      2. PHP经过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);
      3. PHP总共有三个模块:内核、Zend引擎、以及扩展层;
      4. PHP内核用来处理请求、文件流、错误处理等相关操做;
      5. Zend引擎(ZE)用以将源文件转换成机器语言,而后在虚拟机上运行它;
      6. 扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操做。好比,咱们须要MySQL扩展来链接MySQL数据库
      7. 当ZE执行程序时可能会须要链接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;
      8. 最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。

三、PHP的开始和结束阶段

开始阶段有两个过程:

     第一个过程:apache启动的过程,即在任何请求到达以前就发生。是在整个SAPI生命周期内(例如Apache启动之后的整个生命周期内或者命令行程序整个执行过程当中)的开始阶段(MINIT),该阶段只进行一次.。启动Apache后,PHP解释程序也随之启动; PHP调用各个扩展(模块)的MINIT方法,从而使这些扩展切换到可用状态。看看php.ini文件里打开了哪些扩展吧; MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其余请求。 模块在这个阶段能够进行一些初始化工做,例如注册常量, 定义模块使用的类等等.典型的的模块回调函数MINIT方法以下:

 

 

[cpp]  view plain  copy
 
 print?
  1. PHP_MINIT_FUNCTION(myphpextension) { /* Initialize functions, classes etc */ }  
  2. {  
  3.     // 注册常量或者类等初始化操做  
  4.     return SUCCESS;   
  5. }  

     第二个过程发生在请求阶段,当一个页面请求发生时.则在每次请求以前都会进行初始化过程(RINIT请求开始).

请求到达以后,SAPI层将控制权交给PHP层,PHP初始化本次请求执行脚本所需的环境变量,例如建立一个执行环境,包括保存php运行过程当中变量名称和变量值内容的符号表. 以及当前全部的函数以及类等信息的符号表.例如是Session模块的RINIT,若是在php.ini中启用了Session 模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入;  而后PHP会调用全部模块RINIT函数,即“请求初始化”。 在这个阶段各个模块也能够执行一些相关的操做, 模块的RINIT函数和MINIT函数相似 ,RINIT方法能够看做是一个准备过程,在程序执行之间就会自动启动。

 

[cpp]  view plain  copy
 
 print?
  1. PHP_RINIT_FUNCTION(myphpextension)  
  2. {  
  3.     // 例如记录请求开始时间  
  4.     // 随后在请求结束的时候记录结束时间.这样咱们就可以记录下处理请求所花费的时间了  
  5.     return SUCCESS;   
  6. }  

结束阶段分为两个环节:

请求处理完后就进入告终束阶段, 通常脚本执行到末尾或者经过调用exit()或者die()函数,PHP都将进入结束阶段. 和开始阶段对应,结束阶段也分为两个环节,一个在请求结束后(RSHUWDOWN),一个在SAPI生命周期结束时(MSHUTDOWN).
 
第一个环节:请求处理完后结束阶段:请求处理完后就进入告终束阶段,PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。 RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每一个变量调用unset函数。典型的RSHUTDOWN方法以下:
[cpp]  view plain  copy
 
 print?
  1. PHP_RSHUTDOWN_FUNCTION(myphpextension)  
  2. {  
  3.     // 例如记录请求结束时间, 并把相应的信息写入到日至文件中.  
  4.     return SUCCESS;   
  5. }  

第二个环节:最后,全部的请求都已处理完毕,SAPI也准备关闭了, PHP调用每一个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。(这个是对于CGI和CLI等SAPI,没有“下一个请求”,因此SAPI马上开始关闭。)

典型的RSHUTDOWN方法以下:

 

[plain]  view plain  copy
 
 print?
  1. PHP_MSHUTDOWN_FUNCTION(extension_name) {   
  2.     /* Free handlers and persistent memory etc */   
  3.     return SUCCESS;   
  4. }  

 

这样,整个PHP生命周期就结束了。要注意的是,只有在服务器没有请求的状况下才会执行“启动第一步”和“关闭第二步”。

SAPI运行PHP都通过下面几个阶段:
       一、模块初始化阶段(Module init)     :
           即调用每一个拓展源码中的的PHP_MINIT_FUNCTION中的方法初始化模块,进行一些模块所需变量的申请,内存分配等。
        二、请求初始化阶段(Request init)  :
           即接受到客户端的请求后调用每一个拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP脚本的执行环境。
        三、执行PHP脚本
        四、请求结束(Request Shutdown) 
          这时候调用每一个拓展的PHP_RSHUTDOWN_FUNCTION方法清理请求现场,而且ZE开始回收变量和内存。
        五、关闭模块(Module shutdown)     :
           Web服务器退出或者命令行脚本执行完毕退出会调用拓展源码中的PHP_MSHUTDOWN_FUNCTION 方法

 

四、单进程SAPI生命周期

 

CLI/CGI模式的PHP属于单进程的SAPI模式。这类的请求在处理一次请求后就关闭。也就是只会通过以下几个环节: 开始 - 请求开始 - 请求关闭 - 结束 SAPI接口实现就完成了其生命周期。如图所示:

                                      

 

 

五、多进程SAPI生命周期

 

一般PHP是编译为apache的一个模块来处理PHP请求。Apache通常会采用多进程模式, Apache启动后会

fork出多个子进程,每一个进程的内存空间独立,每一个子进程都会通过开始和结束环节, 不过每一个进程的开始阶

段只在进程fork出来以来后进行,在整个进程的生命周期内可能会处理多个请求。 只有在Apache关闭或者进程

被结束以后才会进行关闭阶段,在这两个阶段之间会随着每一个请求重复请求开始-请求关闭的环节。 

如图所示:

                                     

 

六、多线程的SAPI生命周期

多线程模式和多进程中的某个进程相似,不一样的是在整个进程的生命周期内会并行的重复着 请求开始-请求关闭的环节.

 

 

在这种模式下,只有一个服务器进程在运行着,但会同时运行不少线程,这样能够减小一些资源开销,向Module init和Module shutdown就只须要运行一遍就好了,一些全局变量也只须要初始化一次,由于线程独具的特质,使得各个请求之间方便的共享一些数据成为可能。

 多线程工做方式以下图

                              

 

七、Apache通常使用多进程模式prefork

        在Linux下使用#http –l 命令能够查看当前使用的工做模式。也可使用#apachectl -l命令。
        看到的prefork.c,说明使用的prefork工做模式。

        prefork 进程池模型,用在 UNIX 和相似的系统上比较多,主要是因为写起来方便,也容易移植,还不容易出问题。要知道,若是采用线程模型的话,用户线程、内核线程和混合型线程有不一样的特性,移植起来就麻烦。prefork 模型,即预先 fork() 出来一些子进程缓冲一下,用一个锁来控制同步,链接到来了就放行一个子进程,让它去处理。

 

    prefork MPM 使用多个子进程,每一个子进程只有一个线程。每一个进程在某个肯定的时间只能维持一个链接。在大多数平台上,Prefork MPM在效率上要比Worker MPM要高,可是内存使用大得多。prefork的无线程设计在某些状况下将比worker更有优点:他可以使用那些没有处理好线程安全的第三方模块,并 且对于那些线程调试困难的平台而言,他也更容易调试一些。
相关文章
相关标签/搜索