3

fpm源码之初探【原创】

 2 years ago
source link: http://xwxz.github.io/php-fpm-01/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文在php-7.2.0版本下进行分析。主要介绍fpm目录文件,并介绍fpm入口文件。

1、fpm目录

fpm位于php_src/sapi目录下

.
├── CREDITS //fpm名称和贡献者列表
├── LICENSE //声明
├── Makefile.frag //make时使用的文件
├── config.m4 //构建fpm扩展的配置文件
├── fpm //fpm源码目录
├── init.d.php-fpm.in //fpm提供给init.d程序使用的配置
├── php-fpm.8.in
├── php-fpm.conf.in //fpm运行时的配置文件
├── php-fpm.service.in //fpm提供给systemd管理时使用的配置
├── status.html.in // fpm运行时状态查看页面
├── tests //测试fpm的脚本
└── www.conf.in //fpm运行时配置,这个主要是pool池配置管理

linux管理进程有两种常见方式,centOS7以下,基本采用init.d来管理操作系统的启动,而centOS7开始使用systemd来管理系统的启动,关于这两种启动方式,超出本文所讲述的范围,这里只给出相关链接。systemd,阮一峰老师的一篇文章,讲解的非常浅显易懂,Systemd入门。init这里没有找到比较好的,给出维基百科的解释。https://zh.wikipedia.org/wiki/Init

之所以要提及init和systemd,主要是在我们的fpm目录下,为这两个程序提供了相应的管理配置。具体配置这里不展开详述了,感兴趣的可以了解相关资料。

fpm目录

存放fpm源码的目录,fpm采用c语言编写,每个文件都是成对出现的,.c.h文件,其详细结构如下:

php-7.2.0/sapi/fpm
.
├── events //fpm事件处理相关源码
│   ├── devpoll.c
│   ├── devpoll.h
│   ├── epoll.c
│   ├── epoll.h
│   ├── kqueue.c
│   ├── kqueue.h
│   ├── poll.c
│   ├── poll.h
│   ├── port.c
│   ├── port.h
│   ├── select.c
│   └── select.h
├── fpm.c //fpm主程序实现
├── fpm.h //fpm主程序头文件
├── fpm_arrays.h //fpm数组相关
├── fpm_atomic.h //fpm原子操作相关
├── fpm_children.c //fpm子进程相关
├── fpm_children.h
├── fpm_cleanup.c
├── fpm_cleanup.h //fpm内存清理
├── fpm_clock.c
├── fpm_clock.h //fpm时钟管理
├── fpm_conf.c
├── fpm_conf.h //fpm运行时配置解析,包括运行方式、进程池等的配置
├── fpm_config.h //fpm计数器配置
├── fpm_env.c
├── fpm_env.h //fpm环境变量
├── fpm_events.c
├── fpm_events.h //fpm事件处理相关
├── fpm_log.c
├── fpm_log.h //fpm日志相关
├── fpm_main.c //fpm入口文件
├── fpm_php.c
├── fpm_php.h //fpm中php与zend的中间交互
├── fpm_php_trace.c
├── fpm_php_trace.h //fpm执行栈
├── fpm_process_ctl.c
├── fpm_process_ctl.h //fpm进程控制
├── fpm_request.c
├── fpm_request.h //fpm请求处理
├── fpm_scoreboard.c
├── fpm_scoreboard.h //fpm的计数器,主要用于管理work
├── fpm_shm.c
├── fpm_shm.h //fpm共享内存分配处理
├── fpm_signals.c
├── fpm_signals.h //fpm信号处理
├── fpm_sockets.c
├── fpm_sockets.h //fpm套接字处理
├── fpm_status.c
├── fpm_status.h //fpm状态管理
├── fpm_stdio.c
├── fpm_stdio.h //fpm输入输出管理
├── fpm_str.h //fpm字符串复制
├── fpm_systemd.c
├── fpm_systemd.h //fpm与systemd通信管理
├── fpm_trace.c
├── fpm_trace.h //fpm调用栈相关
├── fpm_trace_mach.c //fpm master进程检测相关
├── fpm_trace_pread.c //fpm进程状态检测相关
├── fpm_trace_ptrace.c
├── fpm_unix.c
├── fpm_unix.h //处理unix文件系统相关
├── fpm_worker_pool.c
├── fpm_worker_pool.h //fpm 工作进程池相关
├── zlog.c
└── zlog.h //fpm日志记录

fpm入口程序

FPM(FastCGI Process Manager)是PHP FastCGI运行模式的一个进程管理器,从它的定义可以看出,FPM的核心功能是进程管理,那么它用来管理什么进程呢?接下来我们将从入口程序开始,一点一点揭开其神秘面纱。分析一个程序,找准入口是关键,找到入口后根据主脉络往下捋即可。按照这样的指导思想,那我们就开始吧。

php_src/sapi/fpm/fpm/fpm_main.c:fpm入口程序,找到main入口函数,我们先来看下fpm启动执行的主要流程:

int main(int argc, char *argv[])
{
...
//注册SAPI:将全局变量sapi_module设置为cgi_sapi_module
sapi_startup(&cgi_sapi_module);
...
//执行php_module_starup()
if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
return FPM_EXIT_SOFTWARE;
}
...
//初始化
if(0 > fpm_init(...)){
...
}
...
fpm_is_running = 1;

fcgi_fd = fpm_run(&max_requests);//后面都是worker进程的操作,master进程不会走到下面,因为在fpm_run中已经根据work_pool进行子进程fork了
parent = 0;
...
request = fpm_init_request(fcgi_fd);//初始化请求,开辟内存空间、设置请求相关的环境变量、绑定socket等
zend_first_try {//开始一个try
while (EXPECTED(fcgi_accept_request(request) >= 0)) {//阻塞等待请求到来,关于fpm如何接受请求,后面会有文章专门讲解
...
init_request_info();//初始化请求结构,包括PATH_INFO、SCRIPT_NAME、REQUEST_URI、SCRIPT_FILENAME等
fpm_request_info();//将cgi中的请求信息复制到fpm请求中
php_request_startup()//php请求处理开始
fpm_status_handle_request()//处理fpm启动后状态检测,如果状态不太对,就跳过后面的处理,结束请求
...
php_fopen_primary_script(&file_handle)//

...
fpm_request_executing();//请求执行
php_execute_script(&file_handle);//执行脚本
...
fpm_request_end();//请求执行结束
php_request_shutdown((void *) 0);//请求关闭
...
requests++;
if (UNEXPECTED(max_requests && (requests == max_requests))) {//判断一个子进程处理的请求数,如果达到处理上线就光荣退出
fcgi_request_set_keep(request, 0);
fcgi_finish_request(request, 0);
break;
}
...
}
fcgi_destroy_request(request);//销毁fpm请求
fcgi_shutdown();//fcgi关闭
} zend_catch {
exit_status = FPM_EXIT_SOFTWARE;
} zend_end_try();

php_module_shutdown();//关闭模块
...
return exit_status;//进程退出
}

总结一些入口文件干的事情:

  1. 初始化数据结构
  2. 解析命令行参数
  3. 初始化fpm_init
  4. 执行fpm_run
  5. 启动监听端口并处理请求(此处主进程是不会走到的)

下面一篇文章将会讲解fpm的进程是如何管理的


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK