生命周期和Zend引擎
SAPI接口(Server Application Programming Interface)是php具体应用的编程接口。php开始执行会经历两个阶段:处理请求之前的开始阶段和请求之后的结束阶段。开始阶段有两个过程MINIT(初始化模块阶段)和RINIT(激活模块阶段)。在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序的整个执行过程中),MINIT只执行一次.RINIT发生在请求阶段,例如通过url请求某个页面,则在每次请求之前都会进行模块激活.如果php注册了一些扩展模块,则在MINIT阶段会回调所有模块的MINIT函数.
Zend引擎:
Zend引擎是PHP实现的核心,提供了语言上的基础设施。如:PHP的语法实现,脚本的编译运行环境,扩展机制以及内管理等。
第二节SAPI概述
在各个服务器抽象层之间遵守着相同的约定,这里我们称之为SAPI接口.每个SAPI实现都是一个_sapi_module_struct结构体变量.SAPI的简单示意图
上层调用---->>SAPI层------>>last使对方开始的开发(CGI/FASTCGI Apache Embed)
目前PHP内置的很多SAPI实现都已不在维护或者变的有些非主流了.PHP社区目前正在考虑将一些SAPI移除代码库
Apache模块:
Apache支持许多特性,大部分通过模块扩展实现.常见的模块包括mod_auth(权限验证),mod_ssl(SSL和TLS支持),mod_rewrite(URL重写)等.一些通用的语言也支持以Apache模块的方式与Apache集成,如Perl,Python,Tcl和PHP等.
当PHP需要在Apache服务器下运行时,一般来说,他可以mod_php5模块的形式集成,此时mod_php5模块的作用是接受Apache传递过来的PHP文件请求.如果我在Apache启动前在其配置文件中配置好了PHP模块(mod_php5),PHP通过注册apache2的ap_hook_post_config挂钩,在Apache启动的是偶启动此模块以接受PHP文件的请求.
除了这种启动时的加载方式,Apache的模块可以在运行时侯动态加载,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器.有关动态加载,稍后再叙.
嵌入式:
嵌入式PHP类似CLI,也是SAPI接口的另一个实现.一般情况下,他的一个请求的生命周期也会和其他的SAPI一样:模块初始化->请求初始化->处理请求->关闭请求->关闭模块.对于嵌入式PHP我们了解很少,或者根本用不到,资料很很少.例如很多游戏中使用Lua语言作为粘合语言,或者作为扩展游戏的脚本语言,类似的哦,浏览器中的Javascript语言就是迁入在浏览器中的.只是目前很少有应用将PHP作为嵌入式语言来使用,PHP的强项目前还是在Web开发方面.
*****FastCGI简介**********
CGI全称是"通用网关接口 ",它可以让一个客户端,从网页浏览器向执行在Web服务器上的程序请求数据.CGI描述了客户端和这个程序之间传输数据的一种标准.CGI的一个目的是要独立于任何语言的,所以CGI可以用任何一种语言编写,如php,perl,tcl等.
FastCGI是Web服务器和处理程序之间通信的一种协议,是CGI的一种改进方案,FastCGI 像是一个常住行的CGI,它可以一直执行,在请求到达时不会花费时间去fork一个进程来出来.正是因为它只是一个通信协议,它还支持分布式运算,即FastCGI程序可以在网站服务器以外的主机上执行并且接受来自其他网站的请求.
FastCGI是语言无关的 可伸缩架构的CGI开放扩展,将CGI解释器进程保持在内存中,以此获得较高的性能.
一般情况下,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中)的下一个链接.
PHP中的CGI实现:
PHP的CGI实现了FastCGI协议,是一个TCP或UDP协议的服务器接受来自Web服务器的请求,当启动时TCP/UDP协议的服务器监听,并接受相关请求进行处理.随后就进入了PHP的生命周期.
以TCP为例,在TCP的服务器端,一般会这样执行几个操作步骤:
1 调用socket函数创建一个TCP用的流式套接字;
2 调用bind函数将服务器地址与前面创建的套接字绑定;
3 调用listen函数将新创建的套接字作为监听,等待客户端发起的链接,当客户端有多个连接连接到这个套接字时,可能需要排队处理;
4 服务器进程调用accept函数进入阻塞状态,直到有客户进程调用connect函数而建立起一个连接;
5 当与客户端创建连接后,服务器调用read_stream函数读取客户的请求;
6 处理完数据后,服务器调用write函数向客户端发送应答.
同样,以读取cookie为例,当我们在CGI环境下,在PHP中调用读取Cookie时,最终获取的数据的位置是在激活SAPI时.他所调用的方法是read_cookies.有SAPI实现来获取cookie,这样各个不同的SAPI就能根据自己的需要来实现一些依赖环境的方法.
所有使用PHP的场合都需要定义自己的SAPI,例如在Apache模块方式中,sapi_module是apache2_sapi_module,其对应read_cookies方法的是php_apache_sapi_read_cookies函数,而在我们这里,读取cookie的函数是sapi_cgi_read_cookies.再次说明定义SAPI结构的理由:统一接口,面向接口的编程,具有更好的扩展性和适应性.
第三节 PHP脚本的执行
前面介绍了PHP的生命周期,PHP的SAPI,SAPI处于PHP整个架构叫上层,而真正脚本的执行主要有Zend引擎来完成.
目前语言可以分为两大类:
第一类是像C/C++、.NET、JAVA之类的编译型语言
第二类比如:PHP、Javascript、Ruby、Python这类解释性语言。一般这些语言都需要一个解释器,有解释器来执行这些源码,实际上这些 语言还是会经过编译环节,只不过他们一般会在运行的时候实时进行编译。
但它们并不是真的直接就能够被机器理解,机器只能理解机器语言,一般这些语言都需要一个解释器,有解释器来执行这些源码,实际上这些语言还是会经过编译环节,只不过他们一般会在运行的时候实时进行编译.为了效率,并不是所有语言在每次执行的时候都会重新编译一遍,比如PHP的各种opcode缓存扩展(如APC, xcache , eAccelerator等).
PHP的脚本的执行也需要一个解释器,比如在命令行下的php程序,或者apache的mod_php模块等等.程序如何执行:
1 传递给php程序需要执行的文件,php程序完成基本的准备工作后启动PHP及Zend引擎,加载注册的扩展模块.
2 初始化完成后读取脚本文件,Zend引擎对脚本文件进行词法分析,语法分析.然后编译成opcode执行.如过安装了apc之类的opcode缓存,编译环节可能会被跳过而直接从缓存中读取opcode(字节码)执行.
来源:https://www.cnblogs.com/qinpeirong/archive/2012/09/20/2695036.html