一、概述PHP-FPM启动后,master进程会落入event_loop(0)管理和维护worker进程,fork出来的worker进程会回到main函数开始循环接收,处理请求。一个请求可以概括为三个阶段:请求接收、请求处理和请求结束,下面将详细描述。运行环境:Mac10.14.2+PHP7.3.72、在请求接收阶段锁定listen_socket:因为accept()会造成拥挤问题,所以在调用accept()之前先锁定listen_socket。雷群问题在Linux2.6版本已经解决,当内核收到客户端连接时,只会唤醒等待队列中的第一个进程。获取client_socket:worker进程会调用accept(listen_socket,(structsockaddr*)&sa,&len)从全连接队列中接受一个连接。如果队列中没有连接,它将永远被阻塞。这里的listen_socket是在fcgi_listen()监听中创建的。判断是否允许client_socket:可以满足以下请求之一。client_socket是unix_socket,表示client是allowed_clients列表中的本地客户端地址,通过listen.allowed_clients参数配置allowed_clients等待client_socket上的可读事件发生:在do-while循环中调用poll()来监听client_socket上的可读事件,这里的while条件是while(ret<0&&errno==EINTR);EINTR错误是当被阻塞的poll()被捕获信号打断时产生的错误,因此可以重新执行poll系统调用。读取client_socket中的数据:这是FastCGI协议的一个实现。Nginx会按照FastCGI协议的消息格式发送数据。然后worker进程会根据协议多次read()数据并解析。消息传递大致如下。请参阅这篇关于PHP如何实现FastCGI协议的文章。3.请求处理阶段的初始化worker进程读取上一阶段的请求数据后,接下来会初始化输出相关的栈,初始化编译阶段使用的compiler_globals(CG宏),以及使用的executor_globals(EG宏)在执行阶段。执行每个扩展的PHP_RINIT_FUNCTION函数等ZendVM说到请求处理阶段就不得不提到ZendVM。大家都知道PHP是一种解释型语言,而ZendVM就是PHP的解释器,负责PHP的解析和执行。计算机无法理解PHP代码,但ZendVM可以。对于PHP,ZendVM就像一台真正的“计算机”。这台“计算机”能识别的指令是事先定义好的操作码。在运行时,PHP会被编译成一系列的opcode指令,ZendVM会一条一条调用opcode对应的机器指令,最终完成PHP代码的运行。ZendVM运行词法语法分析生成AST:这一步的目的是生成抽象语法树AST。AST是PHP7引入的概念。在PHP7之前,opcode是经过语法分析后直接生成的。在这个过程中,语法分析器yacc不断调用词法分析器re2c将PHP代码切割成token,然后yacc根据token组合匹配语法规则,最终生成AST。解析AST并生成zend_op_array:这一步的目的是生成zend_op_array,它是编译后所有opline指令的集合,包括编译过程中产生的关键数据。对于ZendVM,zend_op_array是可执行数据。ZendVM实现zend_op_array:zend_op_array作为ZendVM编译器的输出,也是ZendVM执行器的输入。ZendVM执行器在执行过程中会调用opcode对应的handler来完成指令处理,handler就是每一个opcode对应的用C语言编写的处理逻辑。4.在请求结束时,执行用户通过register_shutdown_function()注册的关闭函数,释放资源,清理符号表,销毁超全局变量,重置max_execution_time等。刷新所有缓冲区,执行每个扩展的PHP_RSHUTDOWN_FUNCTION函数。..经过上面的cleanup操作后,worker进程就准备好接收和处理下一个请求了。
