Linux下的IPC非常多,nginx的进程都是有亲缘关系的进程,对于他们的通信我们选择TCP socket进行通信。 TCP socket 用来做进程通信的优点有, 1.socket是文件描写叙述符,操作简单。 2.双向流动。3.另外另一个重要优点:可记录可重现,我们能够用tcpdump抓取信息,方便调试。当然对于进程间大量数据的共享 自然而然的我们採用共享内存。
採用socketpair()函数创造匿名socket 为master process(父进程) 和 work process(子进程) 以及work process 之间的通信。
关于socketpair() 參见这里就可以。
先看看nginx process 的定义
ngx_process.h
typedef struct {
ngx_pid_t pid;
int status;
ngx_socket_t channel[2];//这里就是用来存放 socketpair 的两个描写叙述符
ngx_spawn_proc_pt proc;
void *data;
在nginx_process.c中
ngx_pid_t
ngx_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;
...
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) //注意是在fork之前哦
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
}
...
pid=fork();
...
在运行fork之前,先调用 socketpair()创建一对socket 描写叙述符存放变量ngx_process[s].channel内,在fork()之后,子进程继承了父进程的这一对socket描写叙述符,Nginx此时会把channel[0]给父进程使用,把channel[1]给子进程使用。从这以后就能够实现父子进程的通信。
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
ngx_channel_handler) //子进程把channel增加到监听事件,ngx_channel_handler为事件响应的回调函数
== NGX_ERROR)
{
那么子进程和子进程直接是怎么通信的呢?
实际上子进程work_process之间也是通过这些socket进行通信的 : master父进程每次fork一个新进程的时候都会把这个新进程的信息告知前面已经生成的子进程
在ngx_process_cycle.c中
ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
{
ngx_int_t i;
for (i = 0; i < ngx_last_process; i++) {
if (i == ngx_process_slot
|| ngx_processes[i].pid == -1
|| ngx_processes[i].channel[0] == -1)
{
continue;
}
參数ch包括了刚刚创建的新的子进程pid,进程信息,channel描写叙述符在全局数组中。
只是截至眼下,Nginx并没有子进程向父进程写入信息,子进程间的通信也没有实际用处。
这样子进程直接都有了各自的信息包含socket描写叙述符。这样能够彼此能够通过此完毕通信。
參考书籍:《深入剖析Nginx> 高群凯