还是这张图,本次要验证的是在不同的sapi客户端下,具体流程是怎样的。
测试环境
- Debian 9
- PHP 7.3.1
- Nginx 1.10.3
- Apache 2.4.25
测试方法
在扩展文件的这些方法MINIT、MSHUTDOWN、RINIT、RSHUTDOWN
内部添加输出,通过不同的形式进行调用,查看输出规则。
我开始是在扩展内部封装了个函数,分别在上面列出的函数中调用该函数,向指定的文件内写日志:
void run_log(char *log)
{
FILE *fp = fopen("/tmp/c.log", "a+");
fputs(log, fp);
fclose(fp);
return;
}
这种写法在cli模式下运行正常,但是到fastcgi模式下就不太正常了。问题是这样:
启动php-fpm,日志记录正常。
通过nginx访问一个页面进行调用,nginx会出现如下日志:
2019/08/15 12:17:26 [error] 14922#14922: *77 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 192.168.199.200, server: _, request: "GET /a.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "192.168.199.235"
同时php-fpm也会有如下错误日志:
[15-Aug-2019 12:17:26] WARNING: [pool www] child 17308 exited on signal 11 (SIGSEGV) after 15.726199 seconds from start
[15-Aug-2019 12:17:26] NOTICE: [pool www] child 17315 started
看错误是worker进程莫名退出。
搜索错误中提到的信号SIGSEGV
,其中有一种原因可能是文件权限问题,一看,还真是,因为这个文件的创建和写入涉及到两个用户,一个是php-fpm启动时候的用户,例如root,启动fpm的时候,会创建文件,权限是-rw-r--r--
。另一个是通过nginx访问,最终操作文件是fpm进程的用户,例如www,而www用户对root用户创建的文件是没有写入权限的。所以给日志文件添加权限就可以了。
同理,如果先在cli下调用了,也可能会有这个问题。
除了上面这种方法,也可以直接用PHP的输出APIphp_printf
,跟c的printf
用法一致。
例如:
PHP_RSHUTDOWN_FUNCTION(wzh)
{
php_printf("r shutdown running\n");
return SUCCESS;
}
注:最好不要在扩展内部使用c语言原有的printf
函数,否则可能会有意想不到的结果。
cli
以cli形式的调用没什么特别需要说明的,所有的流程都需要加载一遍,具体输出如下:
m init running
r init running
wzh function running
r shutdown running
m shutdown running
php-fpm
php-fpm是master/worker模型,从性能的角度考虑,一些公共的初始化可能在master进程完成,而请求相关的可能在worker进程来初始化。所以可以猜测MINIT、MSHUTDOWN
是在php-fpm启动和关闭的时候运行的,而RINIT、RSHUTDOWN
是在每个请求中运行的。
验证如下,启动php-fpm进程时候的日志:
m init running
m shutdown running
这里比较纳闷的是为什么会运行MSHUTDOWN
?
http请求之后的日志:
r init running
wzh function running (这个是调用扩展函数的日志 )
r shutdown running
kill掉php-fpm进程的日志:
m shutdown running
到这里可以验证我们的猜测是正确的。
apache2
apache2解析php可以用libapache2-mod-php
模块,这种是方式基于cgi协议。目前apache也支持fastcgi,这种方式的加载同样依赖于php-fpm,所以就不做讨论了。
apache有几种进程模型,php官方推荐Prefork MPM
,这个模型也是master/worker,所以可能也是master进程做模块初始化,worker进程处理请求,跟php-fpm类似。
通过构建扩展,网页访问输出如下:
r init running
wzh function running
r shutdown running
可以看到请求的执行流程与php-fpm一致。MINIT、MSHUTDOWN
因为代码的问题,目前未能验证出来是在进程启动的时候执行的(详见问题2)。
遇到的问题
- apache与nginx的php编译参数差异
我开始测试的web server是nginx,扩展什么的都能正常使用,但是换上apache2后,所有的扩展都不能使用,后来重新编译了php,添加了如下参数
--with-apxs2=/usr/bin/apxs
,如果没有apxs
命令可以通过apt install apache2-dev
来安装。
同样,添加了上述编译参数后编译好的扩展,nginx也不能用。
- 上述扩展中打日志用的
run_log
在apache2下不能正常使用,请求的时候有如下错误,具体原因暂未查到。暂时通过php_printf
代替了。
child pid 57265 exit signal Segmentation fault (11)
- php-fpm下初始化会执行
MSHUTDOWN
的问题 这个问题比较怪的是,如果换成php_printf
代替,就不会有MSHUTDOWN
了,原因暂时没找出来。
(完)
- 本文作者:吴泽辉
- 本文链接:https://mutex.top/posts/586520e9/
- 发表日期:2019年8月16日
- 版权声明:本文章为原创,采用《知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议》进行许可