1. Process management Mode
The PHP-FPM consists of 1 master processes and n worker processes. Where the worker process is forked by the master process.
PHP-FPM has 3 worker process management modes.
1. Static
Call Fpm_children_make (wp,0,0,1) function during initialization to fork out Pm.max_children number of worker processes, and subsequently no longer dynamically increase or decrease the number of worker processes
2. Dynamic
The Fpm_children_make (wp,0,0,1) function is called when initializing to fork out a pm.start_servers number of worker processes, and then the heartbeat event is triggered every 1 seconds fpm_pctl_perform_idle_server _maintenance () to maintain the number of idle Woker processes: If the number of idle worker processes is more than pm.max_spare_servers, the kill process, if less than pm.min_spare_servers, is the fork process.
3. Ondemand
The worker process is not generated when initializing, but registering the event ondemand_event monitoring listening_socket. When Listen_socket receives the request, it first checks to see if there are idle worker processes that have been generated and uses this idle process if it exists, otherwise fork a new process. A heartbeat event triggered every 1 seconds fpm_pctl_perform_idle_server_maintenance () will kill the worker process that has idle time exceeding pm.process_idle_timeout
2. Standard IO
The typical flow of fastcgi is as follows:
(1) Web server (for example, Nginx or Apache) accepts a request. The Web server then connects to the FASTCGI application through a UNIX domain socket or TCP socket.
(2) fastcgi application can choose to accept or reject this connection. If the connection is accepted, the FASTCGI application attempts to read from the stream to a packet
(3) The first packet sent by Web server is Begin_request packet. The Begin_request packet contains a unique REQUEST ID. All subsequent packet of the request are marked with this ID.
Unix system, the standard input file descriptor is 0, the standard output file descriptor is 1, the standard error output file descriptor is 2, the macro is defined as follows:
#define STDIN_FILENO 0
#define Stdout_fileno 1
#define Stderr_fileno 2
PHP-FPM redirects these three standard IO.
In the master process, Stdin_fileno (0) and Stdout_fileno (1) are redirected to "/dev/null". Stderr_fileno (2) redirects to Error_log.
In the worker process, Stdin_fileno (0) redirects to Listening_socket. If Catch_workers_output is no, Stdout_fileno (1) and Stderr_fileno (2) are redirected to "/dev/null". Otherwise, Stdout_fileno (1) redirects to the write end of the 1 pipe, and the FD read end of the pipe is stored on the fd_stdout element of the child node structure corresponding to the child linked list of the master process. Similarly, Stderr_fileno (2) redirects to the write end of 1 pipes, while the read-side FD of the pipe is stored on the Fd_stderr element of the child node structure corresponding to the child linked list of the master process. The above two driven by master processes in the master process of the pipe read the reactor to listen.
3. interprocess communication model
Inter-process communication in PHP-FPM is mainly divided into
1. Communication between the master process and the worker process
There are two pipe lines between the master process and the worker process. The worker process writes information to Stdout_file or Stderr_fileno, and the master process writes log after it receives the information. Master process monitors two pipe with reactor
2. Communication between WEB server and worker processes
Worker process blocking on the accept (listening socket) listening Web server
3. Communication between the WEB server and the master process
When the PM mode is OnDemand, the master process registers the Listening_socket listener event in reactor. When a request arrives, the master process generates a worker process
The process model used by PHP-FPM is the process pool. The worker process inherits the socket FD from the master process socket (), bind (), listen () and blocks directly on the accept (). When there is a request coming, a worker process in the process pool accepts the request. When the worker process finishes executing, it returns to the process pool waiting for the new request. This is actually the leader/follower pattern. In Leader/follower mode, only leader blocking waits and other processes are in sleep. Similarly, in FPM, the new request will only wake up a worker process because the Linux kernel solves the cluster problem of accept (). Here, leader's succession is determined by the Linux kernel (you can, of course, guard the accept code snippet with a mutex to ensure that there is only one leader).
The worker process handles all IO and logic. The master process is responsible for generating and destroying worker processes. The master process's reactor registers three readable events and four timer events. When PM is OnDemand, an additional readable event is registered. Three readable events are 1 signal events, 2 pipe events, respectively.
FPM_EVENT_S structure:
structfpm_event_s {intFD; structtimeval timeout; structTimeval frequency; void(*callback) (structfpm_event_s *, Short,void*); void*Arg; intflags; intindex; Shortwhich; };
Flags represents the type of the event. There are three values for the flags in FPM:
Fpm_ev_read: Readable events
Fpm_ev_persist: Heartbeat Events
Fpm_ev_read | Fpm_ev_edge: A readable event triggered by the edge
which represents which event queue the event is located in. There are two types of values:
Fpm_ev_read: In the readable event queue
Fpm_ev_timeout: In Timer event queue
The structure of the event queue is a doubly linked list
struct fpm_event_queue_s { struct fpm_event_queue_s *prev; struct fpm_event_queue_s *next; struct fpm_event_s *staticstruct fpm_event_queue_s *fpm_event_queue_ Timer = NULL; Static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
The Fpm_event_queue_timer is the Timer event queue, and the FPM_EVENT_QUEUE_FD is a readable event queue. The Timer event queue does not employ the smallest heap, red black tree or event wheel structure, because this queue is very small and there is no need to use these complex structures. However, if the Timer event queue is changed to the ascending list, the performance should be improved.
FD and index are used only in readable events. FD represents the file descriptor being listened to. The value of index is related to which IO multiplexing API is used. In Epoll and select, the value of index is equal to the value of FD. In poll, index is the subscript in which the FD is positioned in the description descriptor fds[]. In the heartbeat event, FD = = -1,index = =-1.
struct Timeval timeout and struct Timeval frequency are only used in heartbeat events. Frequency indicates how many times a heartbeat event is triggered, and timeout indicates the next time the heartbeat event is triggered, usually by now and frequency. In a readable event, these two structures are not set.
Signal_fd_event Events
First look at the signal processing in FPM.
intFpm_signals_init_main ()/* {{{ */{ structSigaction Act; /*Create Socketpair*/ if(0> Socketpair (Af_unix, Sock_stream,0, SP)) {Zlog (Zlog_syserror,"failed to init Signals:socketpair ()"); return-1; } /*set two sockets to Nonblock*/ if(0> fd_set_blocked (sp[0],0) ||0> fd_set_blocked (sp[1],0) {zlog (Zlog_syserror,"failed to init signals:fd_set_blocked ()"); return-1; } /*if the program finishes running successfully, the two FD is automatically closed*/ if(0> Fcntl (sp[0], F_SETFD, fd_cloexec) | |0> Fcntl (sp[1], F_SETFD, fd_cloexec) {Zlog (Zlog_syserror,"falied to Init signals:fcntl (F_SETFD, fd_cloexec)"); return-1; } memset (&act,0,sizeof(act)); /*set the signal processing function to Sig_handler*/Act.sa_handler=Sig_handler; /*Add all signals to the signal set*/Sigfillset (&act.sa_mask); /*change the action of the specified signal*/ 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) {zlog (Zlog_syserror,"failed to init signals:sigaction ()"); return-1; } return 0;}
The master process registered Sigterm,sigint,sigusr1,sigusr2,sigchld,sigquit and created Socketpair sp[]. When these signals are received, the master process writes a character representing the signal to sp[1].
[SIGTERM] = ' T ',
[SIGINT] = ' I ',
[SIGUSR1] = ' 1 ',
[SIGUSR2] = ' 2 ',
[Sigquit] = ' Q ',
[SIGCHLD] = ' C '
Sp[0] is the Signal_fd_event event monitoring FD. The callback function of the event reacts differently to different signals (from sp[0] to the characters that represent the signals).
Signal SIGCHLD:
Call Fpm_children_bury (); This function calls Waitpid () to parse the status of the child process and, depending on the circumstances, whether to restart the child process.
Signal SIGINT, SIGTERM:
Call Fpm_pctl (fpm_pctl_state_terminating, fpm_pctl_action_set); Change the status of FPM to terminating
Signal Sigquit
Call Fpm_pctl (fpm_pctl_state_finishing, Fpm_pctl_action_set); Change the status of FPM to finishing
Signal SIGUSR1
Call Fpm_stdio_open_error_log (1) To restart the error log file
Call Fpm_log_open (1) To restart Access log file and restart all child processes
Signal SIGUSR2
Call Fpm_pctl (fpm_pctl_state_reloading, Fpm_pctl_action_set); Change the status of FPM to reloading
Signal processing for child processes
The child process closes the Socketpair and resets the SIGTERM,SIGINT,SIGUSR1,SIGUSR2,SIGHLD to the default action, setting Sigquit to soft quit.
intFpm_signals_init_child ()/* {{{ */{ structSigaction Act, ACT_DFL; memset (&act,0,sizeof(act)); memset (&ACT_DFL,0,sizeof(ACT_DFL)); Act.sa_handler= &Sig_soft_quit; //a signal arrives when the system call or library function is blocked. The system returns an error by default and sets errno to Eintr. Set to auto restart hereAct.sa_flags|=Sa_restart; Act_dfl.sa_handler=SIG_DFL; Close (sp[0]); Close (sp[1]); if(0> sigaction (SIGTERM, &ACT_DFL,0) ||0> sigaction (SIGINT, &ACT_DFL,0) ||0> sigaction (SIGUSR1, &ACT_DFL,0) ||0> sigaction (SIGUSR2, &ACT_DFL,0) ||0> sigaction (SIGCHLD, &ACT_DFL,0) ||0> sigaction (sigquit, &act,0) {zlog (Zlog_syserror,"failed to init child signals:sigaction ()"); return-1; } zend_signal_init (); return 0;}
One is the readable event queue. The second is the timer queue.
Linux--source code parsing and modeling for PHP-FPM