深入了解FPM(二)

作者:有用网
接下来看下fpm的启动流程,从 main() 函数开始:
//sapi/fpm/fpm/fpm_main.c
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进程不
会走到下面
parent = 0;
...
}
fpm_init() 主要有以下几个关键操作:
__(1)fpm_conf_init_main():__ 
解析php-fpm.conf配置文件,分配worker pool内存结构并保存到全局变量中:fpm_worker_all_pools,各worker pool配置解析到 fpm_worker_pool_s->config中。

__(2)fpm_scoreboard_init_main():__ 分配用于记录worker进程运行信息的共享内存,按照worker pool的最大worker进程数分配,每个worker pool分配一个 fpm_scoreboard_s结构,pool下对应的每个worker进程分配一个fpm_scoreboard_proc_s 结构,各结构的对应关系如下图。 

__(3)fpm_signals_init_main():__ 
static int sp[2];
int fpm_signals_init_main()
{
    struct sigaction act;
    //创建一个全双工管道
    if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
        return -1;
    }
    //注册信号处理handler
    act.sa_handler = sig_handler;
    sigfillset(&act.sa_mask);
    if ( 0 > sigaction(SIGTERM, &act, 0) ||
       0 > sigaction(SIGINT, &act, 0) ||
       0 > sigaction(SIGUSR1, &act, 0) ||
       0 > sigaction(SIGUSR2, &act, 0) ||
       0 > sigaction(SIGCHLD, &act, 0) ||
       0 > sigaction(SIGQUIT, &act, 0)) {
       return -1;
    }
    return 0;
}
这里会通过 socketpair() 创建一个管道,这个管道并不是用于master与worker进程通信的,它只在master进程中使用,具体用途在稍后介绍event事件处理时再作说明。另外设置master的信号处理handler,当master收到SIGTERM、SIGINT、SIGUSR1、SIGUSR2、SIGCHLD、SIGQUIT这些信号时将调用 sig_handler() 处理: 
static void sig_handler(int signo)
{
    static const char sig_chars[NSIG + 1] = {
        [SIGTERM] = 'T',
        [SIGINT] = 'I',
        [SIGUSR1] = '1',
        [SIGUSR2] = '2',
        [SIGQUIT] = 'Q',
        [SIGCHLD] = 'C'
    };
    char s;
    ...
    s = sig_chars[signo];
    //将信号通知写入管道sp[1]
    write(sp[1], &s, sizeof(s));
    ...
}
__(4)fpm_sockets_init_main()__ 
创建每个worker pool的socket套接字。
__(5)fpm_event_init_main():__
启动master的事件管理,fpm实现了一个事件管理器用于管理IO、定时事件,其中IO事件通过kqueue、epoll、poll、select等管理,定时事件就是定时器,一定时间后触发某个事件。
fpm_init() 初始化完成后接下来就是最关键的 fpm_run() 操作了,此环节将fork子进程,启动进程管理器,另外master进程将不会再返回,只有各worker进程会返回,也就是fpm_run() 之后的操作均是worker进程的。
int fpm_run(int *max_requests)
{
    struct fpm_worker_pool_s *wp;
    for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
        //调用fpm_children_make() fork子进程
        is_parent = fpm_children_create_initial(wp);
        if (!is_parent) {
            goto run_child;
        }
    }
    //master进程将进入event循环,不再往下走
    fpm_event_loop(0);
    run_child: //只有worker进程会到这里
    *max_requests = fpm_globals.max_requests;
    return fpm_globals.listening_socket; //返回监听的套接字
}
在fork后worker进程返回了监听的套接字继续main()后面的处理,而master将永远阻塞在fpm_event_loop(),接下来分别介绍master、worker进程的后续操作。 

关键字
FPM

阅读 41 发布于 2021-08-18