当前位置: 首页 > 后端技术 > PHP

PHP内核探秘——进程管理

时间:2023-03-29 15:45:56 PHP

进程管理方式首先我们来了解一下PHP的三种不同的进程管理方式:static:静态管理进程。启动时,master根据pm.max_children配置fork出相应数量的工作进程,即工作进程是固定的。dynamic:动态管理过程。fpm启动时,会根据pm.start_servers初始化一定数量的工作进程。运行过程中,如果master发现空闲工作进程低于pm.min_spare_servers配置数(说明请求多,工作无法处理),会主动fork工作进程。但是worker总数不能超过pm.max_children,如果发现空闲worker数量超过配置的pm.max_spare_servers数量(说明空闲worker较多),master会杀掉部分worker进程,避免占用太多资源。ondemand:这种方式一般很少使用。启动时不分配worker进程,通知master进程forkworker进程,直到有请求。worker总数不超过pm.max_children,worker进程处理完成后不会立即退出,当空闲时间超过pm.process_idle_timeout,则退出。下面看一下fpmmaster执行fpm_run()后进入fpm_event_loop()的逻辑。就是在fpm_init()阶段通过socketpair()创建管道sp[0]//当sp[0]可读时,会回调fpm_got_signal()fpm_event_set(&signal_fd_event,fpm_signals_get_fd(),FPM_EV_READ,&fpm_got_signal,NULL);fpm_event_add(&signal_fd_event,0);//如果在php-fpm.conf中配置了request_terminate_timeout,则开始心跳检查if(fpm_globals.heartbeat>0){fpm_pctl_heartbeat(NULL,0,NULL);}//定时触发进程管理fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL,0,NULL);//进入事件循环,master进程会阻塞在这里while(1){...//等待IO事件ret=module->wait(fpm_event_queue_fd,timeout);...//检查定时器事件...}}这是master的整体处理。它的进程管理主要依赖于几个注册的事件。接下来,我们将详细分析这些事件的作用。(1)sp[1]管道可读事件:在fpm_init()阶段,master创建了一个全双工管道:sp,然后在这里创建了一个sp[0]可读事件,当sp[0]可读时,它将由fpm_got_signal()处理。当数据写入sp[1]时,sp[0]将是可读的。那么什么时候可以向sp[1]写入数据呢?上面说到:当master收到寄存信号后,会写sp[1]端,此时会触发sp[0]可读事件。这个事件被master用来处理信号。下面根据master注册的signals来一一看一下不同的用法:SIGINT/SIGTERM/SIGQUIT:退出fpm。master收到退出信号后,会向所有worker进程发送退出信号,然后master退出SIGUSR1:重新加载日志文件。在生产环境中,日志通常会被切割,切割后会生成一个新的日志文件。如果fpm不重新加载,将无法继续写入日志。这时候需要发一个USR1给master。信号SIGUSR2:重启fpm,首先master也会向所有worker进程发送退出信号,然后master调用execvp()重启fpm,最后老master退出SIGCHLD:这个信号由子进程退出时的操作系统是的,当子进程退出时,内核将子进程设置为僵尸状态。这个进程称为僵尸进程。它只保留了一些最小的内核数据结构,以便父进程可以查询子进程的退出状态。只有当父进程调用wait或waitpid函数查询子进程的退出状态时,子进程才会终止。在fpm中,当worker进程因为异常原因(比如coredump)退出而不是master主动杀掉时,master会收到这个信号。这时候父进程会调用waitpid()检查子进程的退出情况,然后检查是否需要重新fork新的worker。具体处理逻辑在fpm_got_signal()函数中,这里不再一一列举。(2)fpm_pctl_perform_idle_server_maintenance_heartbeat():这是进程管理实现的主要事件。master启动一个定时器,每1s触发一次。主要用于动态和按需模式下的worker管理。master会定时检查每个workerpool的worker进程数,通过这个定时器来控制worker的数量,处理逻辑如下:staticvoidfpm_pctl_perform_idle_server_maintenance(structtimeval*now){for(wp=fpm_worker_all_pools;wp;wp=wp->next){structfpm_child_s*last_idle_child=NULL;//空闲时间最长的workerintidle=0;//空闲工人数intactive=0;//忙worker的个数for(child=wp->children;child;child=child->next){//根据fpm_scoreboard_proc_s->request_stage判断worker进程if(fpm_request_is_idle(child)){//查找空闲时间最长的工人...idle++;}else{主动++;}}...//按需模式if(wp->config->pm==PM_STYLE_ONDEMAND){if(!last_idle_child)continue;fpm_request_last_activity(last_idle_child,&last);fpm_clock_get(&now);if(last.tv_secconfig->pm_process_idle_timeout){//如果空闲时间最长的worker超过process_idle_timeout,则kill该workerlast_idle_child->idle_kill=1;fpm_pctl_kill(last_idle_child->pid,FPM_PCTL_QUIT);}继续;}//dynamicif(wp->config->pm!=PM_STYLE_DYNAMIC)continue;if(idle>wp->config->pm_max_spare_servers&&last_idle_child){//空闲worker太多,killlast_idle_child->idle_kill=1;fpm_pctl_kill(last_idle_child->pid,FPM_PCTL_QUIT);wp->idle_spawn_rate=1;继续;}if(idleconfig->pm_min_spare_servers){//空闲worker太少,如果worker总数没有达到max,则fork...}}}(3)fpm_pctl_heartbeat():该事件用于限制worker处理单个请求的最大时间,php-fpm.conf中有个request_terminate_timeout配置项。如果一个worker处理一个请求的总时间超过这个值,master会向worker进程发送一个kill-TERM信号,杀死这个worker进程。这个配置的单位是秒,默认值为0。关闭这个机制,fpm打印的慢日志也是在这里做的staticvoidfpm_pctl_check_request_timeout(structtimeval*now){structfpm_worker_pool_s*wp;对于(wp=fpm_worker_all_pools;wp;wp=wp->next){intterminate_timeout=wp->config->request_terminate_timeout;intslowlog_timeout=wp->config->request_slowlog_timeout;结构fpm_child_s*child;if(terminate_timeout||slowlog_timeout){for(child=wp->children;child;child=child->next){//检查当前worker处理的请求是否超时fpm_request_check_timed_out(child,now,terminate_timeout,slowlog_timeout);除了上述事件之外,还有一个事件没有提到,就是ondemand模式下master监听到的新请求到达事件,因为ondemand模式下的fpm并没有预测什么时候它在开始创建worker的时候,有请求的时候会生成一个子进程,所以需要在请求到来的时候通知master进程。这个事件是在fpm_children_create_initial()时注册的,事件处理函数是fpm_pctl_on_socket_accept()。具体逻辑这里就不展开了,比较容易理解。本文学习参考:[https://www.kancloud.cn/nickb...]