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

简单的讲,就像函数的输入和输出一样,我们通过Linux命令行执行一段PHP代码,本质是Linux的Shell通过PHP的SAPI传入一组参数,Zend引擎执行后,返回给shell,由shell显示出来的过程。同样的,通过Apache调用PHP,通过Web服务器给SAPI传入数据,Zend引擎执行后,返回给Apache,由Apache显示在页面上。

 

图1. PHP架构图 

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提供了一个函数查看当前SAPI接口类型:


[php] view plaincopy在CODE上查看代码片派生到我的代码片

  1. string php_sapi_name ( void )

PHP的运行和加载

无论使用哪种SAPI,在PHP执行脚本前后,都包含一系列事件:Module的Init(MINT)和Shutdown(MSHUTDOWN),Request 的Init(RINT)和Shutdown(RSHUTDOWN)。 第一阶段是PHP模块初始化阶段(MINT),可以初始化扩展内部变量、分配资源和注册资源处理器,在整个PHP实例生命周期内,该过程只执行一次。

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

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


[php] view plaincopy在CODE上查看代码片派生到我的代码片

  1. PHP_MINIT_FUNCTION(memcached) {

  2. zend_class_entry ce;

  3. memcpy(&memcached_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers));

  4. memcached_object_handlers.clone_obj = NULL;     /* 执行了一些类似的初始化操作 */

  5. returnSUCCESS;

  6. }

第二阶段是请求初始化阶段(RINT),在模块初始化并激活后,会创建PHP运行环境,同时调用所有模块注册的RINT函数,调用每个扩展的请求初始化函数 ,设定特定的环境变量、分配资源或执行其他任务,如审核。



[php] view plaincopy在CODE上查看代码片派生到我的代码片

  1. PHP_RINIT_FUNCTION(memcached) {

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

  3. returnSUCCESS;

  4. }


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

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


[php] view plaincopy在CODE上查看代码片派生到我的代码片

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




常见的运行模式


常见的SAPI模式有五种:


  • CLI和CGI模式(单进程模式)

  • 多进程模式

  • 多线程模式

  • FastCGI模式

  • 嵌入式


1. CLI/CGI模式

CLI和CGI都属于单进程模式,PHP的生命周期在一次请求中完成。也就是说每次执行PHP脚本,都会执行第二部分讲的四个INT和Shutdown事件。

图2. CGI/CLI生命周期 

2. 多进程模式(Multiprocess)

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

 

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

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

 

图4. 多进程的生命周期 

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

3. 多线程模式(Multithreaded)

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


图5. 多线程生命周期

4. FastCGI模式

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

 

图6. FastCGI模式生命周期

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

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

5. 内嵌模式(Embedded)

Embed SAPI是一种特殊的SAPI,允许在C/C++语言中调用PHP提供的函数。这种SAPI和CLI模式一样,按照Module Init => Request Init => Request => Request Shutdown => Module Shutdown的模式运行。

Embed SAPI可以调用PHP丰富的类库,也可以实现高级玩法,比如可以查看PHP的OPCODE(PHP执行的中间码,Zend引擎的指令,由PHP代码生成)。

详细请见: http://www.laruence.com/2008/09/23/539.html

SAPI的运行机制

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


[php] view plaincopy在CODE上查看代码片派生到我的代码片

  1. staticsapi_module_struct cgi_sapi_module = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  18. NULL,                           /* Get request time */

  19. NULL,                           /* Child terminate */

  20. STANDARD_SAPI_MODULE_PROPERTIES

  21. };

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

总结

PHP的SAPI是Zend引擎提供的一组标准交互接口,通过注册初始化、析构、输入、输出等接口,我们可以将应用程序运行在Zend引擎上,也可以把PHP嵌入到类似Apache的Web Server中。PHP常见的SAPI模式有五种,CGI/CLI模式、多进程模式、多线程模式、FastCGI模式和内嵌模式。

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

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

本文转自ljianbing51CTO博客,原文链接: http://blog.51cto.com/ljianbing/1604730,如需转载请自行联系原作者

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

  1. 【问底】王帅:深入PHP内核(一)——弱类型变量原理探究

    来源:CSDN    http://www.csdn.net/article/2014-09-15/2821685-exploring-of-the-php 作者:王帅 摘要:PHP作为一门简单而强大 ...

  2. 金三银四之ConcurrentHashMap剖根问底栏目(二)

    文章目录 前言 1.ConcurrentHashMap的get方法 2.ConcurrentHashMap的keyset方法 3.ConcurrentHashMap中被使用频次非常高的tabAt方法 ...

  3. 2684 亿背后的虚拟化技术:双 11 All on 神龙 | 问底中国 IT 技术演进

    作者 | 阿里云神龙团队 杨航.姚捷 在平稳度过2019天猫双11流量峰值后,阿里巴巴正式宣布,双11核心系统已100%跑在阿里云上.中国唯一自研的飞天云操作系统,成功扛住全球最大规模的流量洪峰! 零 ...

  4. 阿里云智能 AIoT 首席科学家丁险峰:阿里全面进军IoT这一年 | 问底中国IT技术演进...

    作者 | 屠敏 受访者 | 丁险峰 来源 | CSDN(ID:CSDNnews) 「忽如一夜春风来,千树万树梨花开.」 从概念的流行.至科技巨头的相继入局.再到诸多应用的落地,IoT 的发展终于在万事 ...

  5. 逃离泄露事件,阿里云安全默认防御大揭秘 | 问底中国 IT 技术演进

    作者 | 黄晓堃 阿里云安全工程师 致谢 | 吴凡 阿里云高级安全工程师,郭伟博 阿里云高级安全工程师 责编 | 屠敏 出品 | CSDN(ID:CSDNnews) 随着越来越多企业上云,我们深刻体会 ...

  6. 阿里云智能 AIoT 首席科学家丁险峰:阿里全面进军 IoT 这一年 | 问底中国 IT 技术演进

    记者 | 屠敏 受访者 | 丁险峰 出品 | CSDN(ID:CSDNnews) 「忽如一夜春风来,千树万树梨花开.」 从概念的流行.至科技巨头的相继入局.再到诸多应用的落地,IoT 的发展终于在万事 ...

  7. 阿里云弹性计算负责人蒋林泉:亿级场景驱动的技术自研之路 | 问底中国 IT 技术演变...

    作者 | 刘丹 出品 | CSDN云计算(ID:CSDNcloud) 近年来随着云计算如火如荼的发展,上云已经成为当前企业的必经路径.但在国内良莠不齐的云服务市场下,云服务器的选型评估长期困扰着企业. ...

  8. 阿里云智能 AIoT 首席科学家丁险峰:阿里全面进军 IoT 这一年 | 问底中国 IT 技术演进...

    记者 | 屠敏 受访者 | 丁险峰 出品 | CSDN(ID:CSDNnews) 「忽如一夜春风来,千树万树梨花开.」 从概念的流行.至科技巨头的相继入局.再到诸多应用的落地,IoT 的发展终于在万事 ...

  9. 【转】【问底】李平:大型网站的灵魂——性能

    [问底]李平:大型网站的灵魂--性能 发表于2014-09-30 14:32| 18645次阅读| 来源个人博客| 38 条评论| 作者李平 问底架构李平 摘要:大型网站打造并不是件容易的事情,即使是 ...

  10. 【问底】徐汉彬:大规模网站架构的缓存机制和几何分形学

    摘要:缓存机制简单总结可以说是空间换时间,被用于提升系统交互的效率.而有趣的是,这种缓存机制令人惊奇并且优美的遵循着"几何分形"的规律,也就是几何分形学中的"自相似性&q ...

最新文章

  1. Android 多语言
  2. crypt函数的使用(仅限LINUX)
  3. oracle 删除补全日志组_【REDO】删除REDO LOG重做日志组后需要手工删除对应的日志文件(转)...
  4. pigeon hole
  5. 【转载】Linux关机命令详解
  6. noi 4982 踩方格
  7. .NET 6 Preview 1 开箱,带你体验新版本
  8. [转载] Python3网络爬虫
  9. vscode 怎么编辑sphinx_如何在vs Code 中编辑和调试Stata程序
  10. OpenShift 4 - 获取能访问API服务的用户认证Token
  11. CSS3+JS实现静态圆形进度条【清晰、易懂】
  12. js基础(2)~元素增删,属性,节点,定时器,date,事件,模块
  13. Paxos 实现日志复制同步(Basic Paxos)
  14. Python新世界(14)-14行Python代码即可去除抖音水印
  15. error: Could not load host key: /etc/ssh/ssh_host_dsa_key
  16. 基础 | 管理视图、序列、同义词
  17. python的语言风格(一)
  18. 一元非线性回归方程(matplotlib)
  19. sscanf 实现_医保 | 好消息!门诊慢病实现山东省内联网结算啦~
  20. Linux /usr/src/kernels 目录为空的解决方法

热门文章

  1. php libev 主动发送,libev学习笔记
  2. 实时公交查询到站播报流量主小程序开发
  3. Show-Doc让你能在公司装一波的接口文档搭建全流程
  4. 需求分析——系统需求的难点
  5. 电池SOC仿真系列-基于UKF算法的电池SOC估算研究
  6. CentOS 7 设置时间和日期
  7. 行业盛况,嘉信-LAToken合作发布会圆满落幕!
  8. 无限速驱动管理工具Driver Genius
  9. 端口隔离和VLAN内ARP代理
  10. iOS开发mac工具