【问底】王帅:深入PHP内核(二)——SAPI探究

2014 年 10 月 21 日5090

第四阶段,小圈科技,当PHP生命周期结束时候,PHP_MSHUTDOWN_FUNCTION对模块进行回收处理,这是每个扩展的模块关闭函数,用于关闭自己的内核子系统。

【问底】王帅:深入PHP内核(二)——SAPI探究

2. 多进程模式(Multiprocess)

什么是PHP模块?通过上面的PHP架构图,在PHP中可以使用get_loaded_extensions 函数来查看所有编译并加载的模块/扩展,相当于CLI模式下的php

-m。

【问底】王帅:深入PHP内核(二)——SAPI探究

PHP_RINIT_FUNCTION(memcached) { 	



/* 执行一些关于请求的初始化 */



return SUCCESS;



}


免费订阅“CSDN云计算(左)和CSDN大数据(右)”微信公众号,实时掌握第一手云中消息,了解最新的大数据进展!

【问底】王帅:深入PHP内核(二)——SAPI探究

【问底】王帅:深入PHP内核(二)——SAPI探究

CSDN发布虚拟化、Docker、OpenStack、CloudStack、数据中心等相关云计算资讯, 分享Hadoop、Spark、NoSQL/NewSQL、HBase、Impala、内存计算、流计算、机器学习和智能算法等相关大数据观点,提供云计算和大数据技术、平台、实践和产业信息等服务。

图1. PHP架构图

图2. CGI/CLI生命周期

【问底】静行:FastJSON实现详解

第三阶段,请求处理完成后,会调用PHP_RSHUTDOWN_FUNCTION进行回收,这是每个扩展的请求关闭函数,执行最后的清理工作。Zend引擎执行清理过程、垃圾收集、对之前的请求期间用到的每个变量执行unset。请求完成可能是执行到脚本完成,也可能是调用die()或exit()函数完成

【问底】王帅:深入PHP内核(二)——SAPI探究

总结

PHP的SAPI是Zend引擎提供的一组标准交互接口,通过注册初始化、析构、输入、输出等接口,我们可以将应用程序运行在Zend引擎上,也可以把PHP嵌入到类似Apache的Web

Server中。PHP常见的SAPI模式有五种,CGI/CLI模式、多进程模式、多线程模式、FastCGI模式和内嵌模式。

Apache2的Worker MPM采用了多线程模型,在一个进程下创建多个线程,在同一个进程地址空间执行。

图4. 多进程的生命周期

【问底】王帅:深入PHP内核(二)——SAPI探究

SAPI是Server Application Programming Interface(服务器应用编程接口)的缩写。PHP通过SAPI提供了一组接口,供应用和PHP内核之间进行数据交互。

图6. FastCGI模式生命周期

常见的运行模式

1. CLI/CGI模式

详细请见:

http://http://www.zjjv.com//ruence.com/2008/09/23/539.html

在一个子进程中,PHP的生命周期是调用MINT启动后,执行多次请求(RINT/RSHUTDOWN),在Apache关闭或进程结束后,才会调用MSHUTDOWN进行回收阶段。

多进程模式可以将PHP内置到Web Server中,PHP可以编译成Apache下的prefork MPM模式和APXS模块,当Apache启动后,会fork很多子进程,每个子进程拥有自己独立的进程地址空间。

关于作者:王帅,腾讯企业QQ SaaS团队Leader。

可以想象CGI的系统开销有多大。每一个Web 请求PHP都必须重新解析php.ini、载入全部扩展并始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。另外,对于数据库和Memcache的持续连接可以工作。

SAPI的运行机制

我们以CGI为例,看一下SAPI的运行机制。

string php_sapi_name ( void )

PHP的运行和加载

无论使用哪种SAPI,在PHP执行脚本前后,都包含一系列事件:Module的Init(MINT)和Shutdown(MSHUTDOWN),Request

的Init(RINT)和Shutdown(RSHUTDOWN)。 第一阶段是PHP模块初始化阶段(MINT),可以初始化扩展内部变量、分配资源和注册资源处理器,在整个PHP实例生命周期内,该过程只执行一次。

3. 多线程模式(Multithreaded)

以PHP的Memcached扩展源代码为例:

图3. 多进程模式生命周期

常见的SAPI模式有五种:

static sapi_module_struct cgi_sapi_module = {     



"cgi-fcgi", /* 输出给php_info()使用 */ "CGI/FastCGI", /* pretty name */



php_cgi_startup, /* startup 当SAPI初始化时,首先会调用该函数 */



php_module_shutdown_wrapper, /* shutdown 关闭函数包装器,它用来释放所有的SAPI的数据结构、内存等,调用php_module_shutdown */



sapi_cgi_activate, /* activate 此函数会在每个请求开始时调用,它会做初始化,资源分配 */



sapi_cgi_deactivate, /* deactivate 此函数会在每个请求结束时调用,它用来确保所有的数据都得到释放 */



sapi_cgi_ub_write, /* unbuffered write 不缓存的写操作(unbuffered write),它是用来向SAPI外部输出数据 */



sapi_cgi_flush, /* flush 刷新输出,在CLI模式下通过使用C语言的库函数fflush实现*/ NULL, /* get uid */



sapi_cgi_getenv, /* getenv 根据name查找环境变量 */



php_error, /* error handler 注册错误处理函数 */



NULL, /* header handler PHP调用header()时候被调用 */



sapi_cgi_send_headers, /* send headers handler 发送头部信息*/



NULL, /* send header handler 发送一个单独的头部信息 */



sapi_cgi_read_post, /* read POST data 当请求的方法是POST时,程序获取POST数据,写入$_POST数组 */



sapi_cgi_read_cookies, /* read Cookies 获取Cookie值 */



sapi_cgi_register_variables, /* register server variables 给$_SERVER添加环境变量 */



sapi_cgi_log_message, /* Log message 输出错误信息 */



NULL, /* Get request time */



NULL, /* Child terminate */



STANDARD_SAPI_MODULE_PROPERTIES



};

由上面代码可见,PHP的SAPI像是面向对象中基类,SAPI.h和SAPI.c包含的函数是抽象基类的声明和定义,各个服务器用的SAPI模式,则是继承了这个基类,并重新定义基类方法的子类。

多进程模型中,每个子进程都是独立运行,没有代码和数据共享,因此一个子进程终止退出和重新生成,不会影响其他子进程的稳定。

《问底》是CSDN云计算频道新建栏目,以实践为本,分享个人对于新时代软件架构与开发的深刻见解。在含有“【问底】”字样标题的文章中,你会看到某个国外IT巨头的架构分享,会看到国内资深工程师对某个技术的实践总结,更会看到一系列关于某个新技术的探索。《问底》邀请对技术具有独特/深刻见解的你一起打造一片只属于技术的天空,详情可邮件至zhonghao@。

【问底】王帅:深入PHP内核(二)——SAPI探究

更多《问底》内容

图5. 多线程生命周期

PHP_MSHUTDOWN_FUNCTION(memcached) { /* 执行关于模块的销毁工作 */ UNREGISTER_INI_ENTRIES(); return SUCCESS; }

了解PHP的SAPI机制意义重大,帮助我们理解PHP的生命周期,并了解如何更好的通过C/C++为PHP编写扩展,并在生命周期中找到提高系统性能的方式。

PHP提供了一个函数查看当前SAPI接口类型:

在我们用的Nginx+PHP-FPM用的就是FastCGI模式,Fastcgi是一种特殊的CGI模式,是一种常驻进程类型的CGI,运行后可以Fork多个进程,不用花费时间动态的Fork子进程,也不需要每次请求都调用MINT/MSHUTDOWN。PHP通过PHP-FPM来管理和调度FastCGI的进程池。Nginx和PHP-FPM通过本地的TCP

Socket和Unix Socket 进行通信。

4. FastCGI模式

PHP提供很多种形式的接口,包括apache、apache2filter、apache2handler、caudium、cgi 、cgi-fcgi、cli、cli-server、continuity、embed、isapi、litespeed、milter、nsapi、phttpd

pi3web、roxen、thttpd、tux和webjames。但是常用的只有5种形式,CLI/CGI(命令行)、Multiprocess(多进程)、Multithreaded(多线程)、FastCGI和Embedded(内嵌)。

PHP-FPM进程管理器自身初始化,启动多个CGI解释器进程等待来自Nginx的请求。当客户端请求达到PHP-FPM,管理器选择到一个CGI进程进行处理,Nginx将CGI环境变量和标准输入发送到一个PHP-CIG子进程。PHP-CGI子进程处理完成后,将标准输出和错误信息返回给Nginx,当PHP-CGI子进程关闭连接时,请求处理完成。PHP-CGI子进程等待着下一个连接。

0 0