In Nginx, a full-duplex communication--socketpair is used between the master process and the worker process. When the Socketpair function executes successfully, it creates a pair of socket pairs that have already been connected, and each of the two communicating processes uses one of the sockets to read and write, enabling communication between the two processes.
View Nginx source code, you can see, the following function created the Socketpair
Ngx_pid_tngx_spawn_process (ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, Char *name, ngx_int_t respawn) {u _long on; ngx_pid_t pid; ngx_int_t s; /. ...... Omitted....... /if (respawn! = ngx_process_detached) {/* Solaris 9 still has no af_local *///Create Socketpair if ( Socketpair (Af_unix, Sock_stream, 0, ngx_processes[s].channel) = =-1) {Ngx_log_error (Ngx_log_alert, CYCL E->log, Ngx_errno, "Socketpair () failed while spawning \"%s\ "", name); return ngx_invalid_pid; }//non-blocking if (ngx_nonblocking (ngx_processes[s].channel[0]) = =-1) {Ngx_close_channel (ngx_processes [S].channel, Cycle->log]; return ngx_invalid_pid; }//non-blocking if (ngx_nonblocking (ngx_processes[s].channel[1]) = =-1) {Ngx_close_channel (ngx_processes [S].channel, Cycle->log]; return ngx_invalid_pid; }//Async on = 1; if (IOCTL (ngx_processes[s].channel[0], fioasync, &on) = =-1) {Ngx_close_channel (Ngx_processes[s].channel, Cycle->log); return ngx_invalid_pid; }//sets the process or process group identity that will receive the Sigio or Sigurg event signal on the file descriptor FD. if (Fcntl (ngx_processes[s].channel[0], f_setown, ngx_pid) = =-1) {Ngx_close_channel (Ngx_processes[s].channel, Cycle->log); return ngx_invalid_pid; }//Set Close_on_exec, when a new process is created through the EXEC function family, the socket of the original process is closed if (Fcntl (ngx_processes[s].channel[0], F_SETFD, Fd_clo EXEC) = =-1) {Ngx_close_channel (Ngx_processes[s].channel, Cycle->log); return ngx_invalid_pid; }//Set Close_on_exec, when a new process is created through the EXEC function family, the socket of the original process is closed if (Fcntl (ngx_processes[s].channel[1], F_SETFD, Fd_clo EXEC) = =-1) {Ngx_close_channel (Ngx_processes[s].channel, Cycle->log); return ngx_invalid_pid; } Ngx_channel = ngx_processes[s].channel[1]; } else {ngx_pRocesses[s].channel[0] =-1; NGX_PROCESSES[S].CHANNEL[1] =-1; } Ngx_process_slot = S; PID = fork (); Switch (PID) {case-1: Ngx_close_channel (Ngx_processes[s].channel, Cycle->log); return ngx_invalid_pid; Case 0://fork succeeds, the child process is created, and the associated socket descriptor is also copied a copy of ngx_pid = Ngx_getpid (); Proc (cycle, data); Break Default:break; } /. ...... Omitted....... /return PID;}
After fork succeeds, the descriptor of the original process will also be copied, if the descriptor is no longer used in the fork process, we need to close it in time.
If we are using the Fork->exec function family to create a new process, we can use a better approach to ensure that the original descriptor is shut down properly and avoid resource leaks. That is, the socket is called FCNTL (fd_cloexec) function in the upper code, setting the socket's properties: When the EXEC function family is called, the socket is automatically closed. Using this method of setting the Fd_cloexec property as soon as the socket was created avoids the manual shutdown of the relevant socket before the exec creation process, especially when a large number of descriptor are created and managed.
Most of the time we do this using the following method:
#include
#include
#include
#include
int main () {pid_t pid; int fds[2]; int Valread, valwrite; if (0 > Socketpair (Af_unix, Sock_stream, 0, FDS)) {return 0; } PID = fork (); if (0 = = pid) {pid = Getpid (); printf ("[%d]-child process Start", PID); Close (Fds[0]); Read write on fds[1] write (fds[1], &valwrite, sizeof (Valwrite)); Read (Fds[1], &valread, sizeof (Valread)); } else if (0 < pid) {PID = Getpid (); printf ("[%d]-parent process Continue", PID); Close (fds[1]); Read write on fds[0] Read (Fds[0], &valread, sizeof (Valread)); Write (Fds[0], &valwrite, sizeof (Valwrite)); } else {printf ("%s", "Fork Failed");
} return 0;}
As you can see, the current process creates a pair of sockets, known as Socketpair, before fork. For this pair of sockets, can be seen as a server-side fds[0], and the other is the client fds[1], through fds[0] and FDS[1] established between the link, we can complete full-duplex communication.
After the fork is executed, the child process is created. In a child process, the Socketpair created by the previous parent process will naturally be replicated as FDS ', which is present in the child process. The parent process continues execution. At this time, the same socketpair will exist in the parent and child processes.
Imagine that we write data to fds[0] in the main process, and the data is read on FDS ' [1] in the child process, thus enabling communication between the parent and child processes. Of course, the data is written on the main process fds[1], and the data written is also read on the child process FDS ' [0]. We actually use, only need to keep a pair of sockets for communication, and the other two sockets can be in the parent process and the child process to shut down.
Of course, if we can pass a socket in FDS to another process in some way, we can also implement Socketpair interprocess communication.
The above describes the Nginx interprocess communication-socketpair, including the aspects of the content, I hope that the PHP tutorial interested in a friend helpful.