PHP-FPM中-D命令的实现众所周知,php-fpm是fastcgi的管理程序。在环境部署中,我们使用php-fpm-D启动fpm进程,从而监听9000端口,用于处理nginx转发的请求任务。fpm开始后,打算整理一篇。本文主要讲-D命令,然后用这个命令来研究如何在Linux下写一个daemon进程。什么是Daemon进程Daemon进程是运行在后台的进程,它独立于控制终端,周期性地执行某些任务或等待某些事件的处理。它在没有用户输入的情况下运行,并为整个系统或用户程序提供服务。一个daemon进程的父进程是init进程,因为它真正的父进程在fork之后子进程退出之前就已经退出了,所以它是一个被init继承的孤儿进程。守护进程是没有控制终端的非交互式程序,因此任何输出,无论是标准输出还是标准错误,都需要特殊处理。关于Daemon进程的一些原则Linux中进程与控制终端的关系、登录会话与进程组进程属于一个进程组,进程组号(GID)就是该进程的进程号(PID)过程组组长。一个登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。控制终端、登录会话和进程组通常继承自父进程。我们的目的是摆脱他们,使他们独立于他们的影响。怎么样?一般有几个步骤需要处理:fork()一个子进程继续执行父进程的任务,同时停止父进程。这使得你的进程从控制端进入后台。离开控制终端、登录会话和进程组。调用setsid()使子进程成为会话领导者。关闭父进程打开的文件描述符。处理SIGCHLD信号。在这4个步骤中,以上两个步骤是必须的,后面一般会根据需要进行处理。PHP-FPM的实现在fpm中实现daemon的方法与上面一致。最后我们会通过跟踪fpm的源码来看具体的实现例子。(代码已删)一切从启动开始/sapi/fpm/fpm/fpm_main.cintmain(intargc,char*argv[]){//接收-D参数,设置initfpm为daemon模式while((c=php_getopt(argc,argv,OPTIONS,&php_optarg,&php_optind,0,2))!=-1){switch(c){case'D':/*daemonize*/force_daemon=1;休息;}}if(0>fpm_init(argc,argv,fpm_config?fpm_config:CGIG(fpm_config),fpm_prefix,fpm_pid,test_conf,php_allow_to_run_as_root,force_daemon,force_stderr)){returnFPM_EXIT_CONFIG;}}intfpm_init(intargc,char**configargv,char*char*prefix,char*pid,inttest_conf,intrun_as_root,intforce_daemon,intforce_stderr){//fpm_conf_init_main:设置fpm配置,其中daemon=1if(0>fpm_php_init_main()||0>fpm_stdio_init_main()||0>fpm_conf_init_main(test_conf,force_daemon)||0>fpm_unix_init_main()||0>fpm_scoreboard_init_主要()||0>fpm_pctl_init_main()||0>fpm_env_init_main()||0>fpm_signals_init_main()||0>fpm_children_init_main()||0>fpm_sockets_init_main()||0>fpm_worker_pool_init_main()||0>fpm_event_init_main()){if(fpm_globals.test_successful){退出(FPM_EXIT_OK);}else{zlog(ZLOG_ERROR,"FPM初始化失败");返回-1;}}}intfpm_unix_init_main(){if(fpm_global_config.daemonize){structtimevaltv;fd_set射频识别;诠释;if(pipe(fpm_globals.send_config_pipe)==-1){zlog(ZLOG_SYSERROR,"创建管道失败");返回-1;}/*然后fork*/pid_tpid=fork();switch(pid){case-1:/*error*/zlog(ZLOG_SYSERROR,"守护进程失败");返回-1;case0:/*孩子们*/break;默认值:/*父级*/FD_ZERO(&rfds);FD_SET(fpm_globals.send_config_pipe[0],&rfds);tv.tv_sec=10;tv.tv_usec=0;zlog(ZLOG_DEBUG,"调用进程正在等待主进程通过fd=%dping",fpm_globals.send_config_pipe[0]);ret=select(fpm_globals.send_config_pipe[0]+1,&rfds,NULL,NULL,&tv);if(ret==-1){zlog(ZLOG_SYSERROR,"选择失败");退出(FPM_EXIT_SOFTWARE);}if(ret){/*数据可用*/intreadval;ret=read(fpm_globals.send_config_pipe[0],&readval,sizeof(readval));if(ret==-1){zlog(ZLOG_SYSERROR,"无法从管道读取");退出(FPM_EXIT_SOFTWARE);}if(ret==0){zlog(ZLOG_ERROR,"没有从管道中读取数据");退出(FPM_EXIT_SOFTWARE);}else{if(readval==1){zlog(ZLOG_DEBUG,"我收到了来自主进程的有效确认,我可以无错退出");fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);退出(FPM_EXIT_OK);}else{zlog(ZLOG_DEBUG,"主进程返回错误!");退出(FPM_EXIT_SOFTWARE);}}}else{/*没有发送日期!*/zlog(ZLOG_ERROR,"masterprocessdidn'tsendbackitsstatus(viathepipetothecallingprocess)");退出(FPM_EXIT_SOFTWARE);}退出(FPM_EXIT_SOFTWARE);}}//使当前子进程成为sessionleadersetsid();}这是实现daemon的核心代码,这里fpm做了两步fork()一个子进程,setsid()将子进程设置为sessionleader关注fpm_globals.send_config_pipe。这是一个作为共享变量的数组。在fpm中,它被用作进程间通信的通道。fork()ing子进程后,父进程等待10秒接收子进程的init。如果子进程的init失败,当失败信号写入Pipeline时,父进程获取管道信息后会返回与子进程相同的错误信息,否则父进程正常返回退出。
