先看下PHP的结构图:
1)Zend Engine
Zend引擎是PHP实现的核心,提供了语言实现上的基础设施。例如:PHP的语法实现,脚本的编译运行环境, 扩展机制以及内存管理等。
2)Extensions
围绕着zend引擎,Extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如MySQL系列)、标准库等都是通过Extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的。
3)SAPI
SAPI(Server Application Programming Interface)指的是PHP具体应用的编程接口。就像PC一样,无论安装哪些操作系统,只要满足了PC的接口规范都可以在PC上正常运行。
4)架构思想
如果php是一辆车,那么车的框架就是php本身,Zend是车的引擎(发动机),Ext下面的各种组件就是车的轮子,SAPI可以看做是公路,车可以跑在不同类型的公路上,而一次php程序的执行就是汽车跑在公路上。因此,我们需要:性能优异的引擎+合适的车轮+正确的跑道。
Apache是Apache软件基金会的一个开放源代码的Web服务器,Apache支持许多特性,大部分通过模块扩展实现。
常见的模块包括mod_auth(权限验证)、mod_ssl(SSL和TLS支持) mod_rewrite(URL重写)等。
下图为Apache的逻辑构成以及与操作系统的关系:
1)Apache的mod_php5模块
当PHP需要在Apache服务器下运行时,可以用mod_php5模块的形式集成。
此时mod_php5模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求, 然后将处理后的结果返回给Apache。
2)Apache的运行过程
Apache的运行分为启动阶段和运行阶段。
在运行阶段,Apache主要工作是处理用户的服务请求。
Apache对HTTP的请求可以分为连接、处理和断开连接三个大的阶段。同时也可以分为11个小的阶段:
Post-Read-Request,URI Translation,Header Parsing,Access Control,Authentication,
Authorization,MIME Type Checking,FixUp,Response,Logging,CleanUp。
3)Apache Hook机制
模块可以在Apache的任何一个处理阶段中挂接(Hook)上自己的处理函数,从而参与Apache的请求处理过程。
1)CGI
CGI全称是“通用网关接口”(Common Gateway Interface),描述了客户端和服务器程序之间传输数据的一种标准。它可以让一个客户端,从网页浏览器向执行在Web服务器上的程序请求数据。CGI用来沟通程序(如PHP, Python, Java)和Web服务器(Apache2, Nginx),理论上任何语言编写的程序都可以通过CGI来提供Web服务。
比如现在请求的是“index.php”,根据配置文件,Apache知道这个不是静态文件,需要去找PHP解析器来处理,那么它会把这个请求简单处理后交给PHP解析器。Apache会传url、查询字符串、POST数据、HTTP header等,而CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。
当web服务器收到“index.php”这个请求后,会启动对应的CGI程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini、载入全部扩展并初始化全部数据结构,然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程,web服务器再把结果返回给浏览器。
2)FastCGI
CGI程序存在性能问题,每次请求都会重复“PHP解析器会解析php.ini、载入全部扩展并初始化全部数据结构”这些步骤。
FastCGI是CGI的一种改进方案。FastCGI像是一个常驻(long-live)型的CGI, 它可以一直执行,在请求到达时不会花费时间去fork一个进程来处理(这是CGI最为人诟病的fork-and-execute模式)。
FastCGI工作流程的通俗版本如下:
1. Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。
2. 当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然是高。
3. 当worker不够用时,master可以根据配置预先启动几个worker等着。
4. 当空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源。
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在此便退出了。
3)PHP-CGI
PHP-CGI是PHP自带的FastCGI管理器。
PHP-CGI的不足:
1. php-cgi变更php.ini配置后需重启php-cgi才能让新的php-ini生效,不可以平滑重启。
2. 直接杀死php-cgi进程,php就不能运行了。(PHP-FPM和Spawn-FCGI就没有这个问题,守护进程会平滑从新生成新的子进程。)
4)PHP-FPM
PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的。
PHP-FPM其实是PHP源代码的一个补丁,旨在将FastCGI进程管理整合进PHP包中。必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。
下图为SAPI的简单示意图:
SAPI(Server abstraction API),它提供了一个接口,使得PHP可以和其他应用进行交互数据。
1)PHP执行的两个阶段,开始和结束
开始阶段:
a. 模块初始化阶段(MINIT), 在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序整个执行过程中), 该过程只进行一次
b. 模块激活阶段(RINIT),该过程发生在请求阶段, 例如通过url请求某个页面,则在每次请求之前都会进行模块激活(RINIT请求开始)
结束阶段:
a. 停用模块(RSHUTDOWN,对应RINIT)
b. 在SAPI生命周期结束(Web服务器退出或者命令行脚本执行完毕退出)时关闭模块(MSHUTDOWN,对应MINIT)
2)单进程SAPI生命周期
CLI/CGI模式的PHP属于单进程的SAPI模式。这类的请求在处理一次请求后就关闭。也就是只会经过如下几个环节:
开始 - 请求开始 - 请求关闭 - 结束。SAPI接口实现就完成了其生命周期。
a. 启动:初始化若干全局变量,初始化若干常量,初始化Zend引擎和核心组件,解析php.ini,全局操作函数的初始化,初始化静态构建的模块和共享模块(MINIT),禁用函数和类
b. 激活:激活Zend引擎,激活SAPI,环境初始化,模块请求初始化
c. 运行:要解析执行的文件,需要做词法分析、语法分析和中间代码生成操作,返回此文件的所有中间代码
d. 关闭:关闭请求的过程是一个若干个关闭操作的集合
e. 结束:flush,关闭Zend引擎
3)多进程SAPI生命周期
通常PHP是编译为apache的一个模块来处理PHP请求。
Apache一般会采用多进程模式, Apache启动后会fork出多个子进程,每个进程的内存空间独立,每个子进程都会经过开始和结束环节, 不过每个进程的开始阶段只在进程fork出来以来后进行,在整个进程的生命周期内可能会处理多个请求。