PHP7内核剖析读书笔记-SAPI


SAPI是PHP框架的接口层,是进入PHP内部的入口。
典型的SAPI有3个:Cli,Fpm,Embed。
此篇主要总结Cli和Fpm。

CLI

CLI----Command Line Interface ,命令行接口用于在命令行下执行PHP脚本。
Cli是单进程的,处理完请求就直接关闭了,生命周期先后经历了模块开始(module_startup),请求开始(request_stratup),解析脚本(execute script),关闭请求(request shutdown),关闭模块(module shutdown)。
关键的处理过程。

main() -> php_cli_startup() -> do_cli() -> php_module_shutdown()

1.main函数执行时首先解析命令行参数,初始化记录SAPI信息的结构体(sapi_module_struct)。
2.完成初始化之后进入module startup阶段。module startup之后进入请求初始化,do_cli()完成请求的处理。
3.PHP脚本执行的时候输入形式有很多种,比如文件路径,文件句柄,文件描述符等等。cli中用的是文件句柄。定义好请求的输入结构后将进行请求初始化操作(request startup),执行PHP脚本。
4.完成脚本后进入request shutdown。
5.do_cli()执行完成后回到main()函数中,进入module shutdown,最后进程退出。

特点:单进程。在执行php -m等查询系统信息的请求时不需要经历PHP请求的生命周期。

FPM

FPM----FastCGI Process Manager 是PHP FastCGI运行模式的一个进程管理器。
PHP实现了FastCGI协议的处理,但是没有实现具体的网络处理。两种常用的网络模型。

  • 多进程模型:一个主进城和多个子进程组成,主进城负责管理子进程。例如nginx。
  • 多线程模型:这种模型通常会由主线程监听,接受请求,子线程处理。例如memcached。
  • 注意:进城拥有独立的地址空间和资源,线程共享进城的地址空间和资源。所以多线程要考虑线程安全,资源冲突的问题。

FPM的基本实现

 1.Fpm是一个多进程的模型,是由一个master和多个worker进程组成的。master进程启动的时候创建socket,由fork出的子进程接受并且处理请求。
 2.master主要作用是管理worker进城,负责fork或者kill掉worker进城。   
 3.worker进程的工作原理的为抢占式的接受请求,接受成功后解析FastCGI,然后执行相应的脚本,处理完毕关闭请求,等待新的连接。所以worker为阻塞式的,一个没有处理完不会去处理下一个请求。这种处理方式不需要考虑并发导致的资源冲突。
 4.master和worker不会直接进行通信,master通过共享内存获取worker进程的信息。

worker请求处理

worker进程返回main()函数后继续向下执行,此后的流程就是worker进程不断接受请求,有请求到达后读取并解析FastCGI协议的数据,解析完成后执行PHP脚本,执行完毕后关闭请求。
worker处理请求生命周期。
1.等待请求。worker进程阻塞在fcgi_accept_request()中等待请求。
2.解析请求。fastcgi请求到达后被worker接受,然后接受并解析请求数据。
3.请求初始化。执行php_request_startup(),此阶段会调用每个扩展的钩子函数。
4.执行PHP脚本。php_execute_script()完成的PHP脚本的编译,执行。
5.关闭请求。执行php_request_shutdown()。
在这里插入图片描述

master进程管理

fpm的三种进程管理方式。
  • 静态模式(static):在启动时master根据pm.max_children配置fork出相应数量的worker进程,此种状态下worker进程数目是固定不变的。
  • 动态模式(dynamic):在Fpm启动时会根据pm.start_servers配置初始化一定数量的worker。运行期间如果master发现空闲worker数低于pm.min_spare_servers配置数,表示请求较多,worker不够,此时会fork worker进程,但是总数不能超过最大worker数目(pm.max_children)。如果master发现空闲worker数据超过了pm.max_spare_servers,则会杀掉一些worker。
  • 按需模式(ondemand):这种模式在启动时不分配worker进程,等到有请求后再通知master进程fork worker子进程。

master在调用fpm_run()后不再返回,会进入fpm_event_loop()事件循环,在这个方法中master将循环处理master注册的几个I/O及定时器。

void fmp_event_loop(int err){
   //注册I/O,定时器时间
   ....
   //进入事件循环,master阻塞在此
   while(1){
   	 ...
   	 //等待I/O事件
   	 ret = module->wait(fpm_event_queue_fd, timeout);
   	 ...
   	 //检查定时器事件
   	 ...
	}
}
具体master注册的重要事件
  1. 信号事件
static struct fmp_event_s signal_fd_event;
//设置监听的fd,事件触发类型及回调函数
fmp_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fmp_got_signal, NULL);
//注册事件
fpm_event_add(&signal_fd_event, 0);

fpm_init()阶段时分配了一个sp管道,当master收到SIGTERM,SIGINT,SIGUSR1,SIGUSR2,SIGCHLD,SIGQUIT这些信号时会把信号写到sp[1]管道中。
fpm_signals_get_fd()返回的是sp[0]。当sp[0]可读时回调fmp_got_signal()进行处理。
当向master发送信号时,首先handler会把信号通知发送到sp[1]管道,触发sp[0]可读事件,回调fpm_got_signal()进行处理。
在这里插入图片描述

  1. 进程检查定时器

fpm_event_loop()中调用fpm_pctl_perform_idle_server_maintenance_heartbeat()注册了一个定时器:
这个定时器是用来定期检查worker进程数的,master通过这个定时器每隔一段时间检测worker数量。根据之前三种策略决定是否fork或者kill。

  1. 执行超时检查定时器

php-fpm.conf中有一个request_terminate_timeout的配置项,如果worker处理一个请求的总时长超过此值,那么master会向此worker进程发送kill -TERM信号杀掉worker进程。
这个功能通过定时器实现的,master每隔一定时间检查所有处理中的worker,如果发现其处理时间达到阈值则杀掉这个worker。另外FPM记录的slow log也是通过这个定时器完成的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章