服务器之家

服务器之家 > 正文

深入理解PHP内核(二)之SAPI探究

时间:2020-11-30 16:14     来源/作者:PHP教程网

在上篇文章给大家介绍了深入了解php内核(一),相信大家通过本文多多少少都学到些知识吧,关于php内核知识继续关注本篇文章。

sapi是server application programming interface(服务器应用编程接口)的缩写。php通过sapi提供了一组接口,供应用和php内核之间进行数据交互。

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

深入理解PHP内核(二)之SAPI探究

图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接口类型:

 

复制代码 代码如下:

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扩展源代码为例:

 
?
1
 
2
3
4
5
6
php_minit_function(memcached) {
 zend_class_entry ce;
 memcpy(&memcached_object_handlers,zend_get_std_object_handlers(), sizeof(zend_object_handlers));
memcached_object_handlers.clone_obj = null; /* 执行了一些类似的初始化操作 */
return success;
}

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

 
?
1
 
2
3
4
php_rinit_function(memcached) {
 /* 执行一些关于请求的初始化 */
 return success;
}

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

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

 
?
1
 
php_mshutdown_function(memcached) { /* 执行关于模块的销毁工作 */ unregister_ini_entries(); return success; }

常见的运行模式

常见的sapi模式有五种:

cli和cgi模式(单进程模式)
多进程模式
多线程模式
fastcgi模式
嵌入式

1. cli/cgi模式

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

 深入理解PHP内核(二)之SAPI探究

图2. cgi/cli生命周期

2. 多进程模式(multiprocess)

多进程模式可以将php内置到web server中,php可以编译成apache下的prefork mpm模式和apxs模块,当apache启动后,会fork很多子进程,每个子进程拥有自己独立的进程地址空间。

  深入理解PHP内核(二)之SAPI探究

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

在一个子进程中,php的生命周期是调用mint启动后,执行多次请求(rint/rshutdown),在apache关闭或进程结束后,才会调用mshutdown进行回收阶段。 
 

深入理解PHP内核(二)之SAPI探究

图4. 多进程的生命周期

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

3. 多线程模式(multithreaded)

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

深入理解PHP内核(二)之SAPI探究

图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 进行通信。

深入理解PHP内核(二)之SAPI探究

图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代码生成)。

详细请见:

sapi的运行机制

我们以cgi为例,看一下sapi的运行机制。

 
?
1
 
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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模式,则是继承了这个基类,并重新定义基类方法的子类。

总结

php的sapi是zend引擎提供的一组标准交互接口,通过注册初始化、析构、输入、输出等接口,我们可以将应用程序运行在zend引擎上,也可以把php嵌入到类似apache的web server中。php常见的sapi模式有五种,cgi/cli模式、多进程模式、多线程模式、fastcgi模式和内嵌模式。

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

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址 2020-08-12
最新idea2020注册码永久激活(激活到2100年)
最新idea2020注册码永久激活(激活到2100年) 2020-07-29
返回顶部