nginx 进程间通信

该博客详细介绍了Nginx如何通过socketpair和fork创建进程间通信的channel机制。在每次fork后,子进程会关闭不需要的套接字,并将可读的channel设置给父进程,确保信息的正确传递。关键代码展示了ngx_start_worker_processes、ngx_spawn_process和ngx_pass_open_channel等函数的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

nginx 进程间通信--- channel方式

主要实现思路:通过socketpair创建出一对双通道的套接字并通过fork的方式实现父子进程间的通信

 

解释一下上图:

第一次fork的时候,图中的红色、绿色、紫色部分并没有,是后来通过sendmsg的方式获取到的。那么第一次fork之前做了什么呢?很简单, 通过socketpair创建一对套接字并放到全局数据中,在fork后 父子进程都会有全局数组中的这对套接字,然后子进程将channel[0]关闭了并将channle[1]设置为可读状态。

 

第二次fork的时候,图中的绿色、紫色部分并没有,是后来通过sendmsg的方式获取到的。第二次在fork之前同样通过socketpair创建一对套接字,并放入全局数组中(此时在数组的第二位置,注意由于是全局数组,此时数组中第一的位置上其实是第一次fork出来的套接字),在fork后父子进程都会有全局数组的相关信息,然后子进程将本次的channel[0]关闭了并将channle[1]设置为可读状态,并遍历数组将之前的channel[1]关闭(读者可以猜想一下这个是为什么,见注1)。 同时,父进程在干什么呢? 他会遍历数组,将本次创建的channel[0], 向每个数组中的channel[0]中发送(通过sendmsg),这样由于第一次fork的时候channel[1]一直是可读的状态,所以第一次fork出来的channel[1]会收到这个channel[0],这样第一次fork时的 红色channel[0]就这样被填充上了

 

第三次fork的时候,同样紫色的部分并没有。 在第三次fork之前同样通过socketpir创建一对套接字,并放入全局数组中(此时在数组的第三位置,注意由于是全局数组,此时数组中第一、第二的位置上其实是第一次,第二次fork出来的套接字),在fork后父子进程都会有全局数组的相关信息,然后子进程将本次的channel[0]关闭了并将channle[1]设置为可读状态,并遍历数组将之前的channel[1]关闭。同时父进程会遍历数组,将本次创建的channel[0], 向每个数组中的channel[0]中发送(通过sendmsg),这样由于第一次fork的时候channel[1]一直是可读的状态,所以第一次fork出来的channel[1]会收到这个channel[0],这样第一次fork时的 绿色channel[0]就这样被填充上了。 循环第二次的时候,第二次fork时channel[1]一直是可读的状态,所以第二次fork出来的channel[1]会收到这个channel[0],这样第二次fork时的 绿channel[0]就这样被填充上了

 

第四次,同上

 

注1:其实是为了防止在向第一次创建的套接字对channel[0]写入数据时,会从第二次fork时继承过来的channel[1](这个是第一次通过socketpair得到的)读取到。

 

 

关键代码:

函数 ngx_start_worker_processes

for (i = 0; i < n; i++) {

        ngx_spawn_process(cycle, ngx_worker_process_cycle,
                          (void *) (intptr_t) i, "worker process", type);

        ch.pid = ngx_processes[ngx_process_slot].pid;
        ch.slot = ngx_process_slot;
        ch.fd = ngx_processes[ngx_process_slot].channel[0];

        ngx_pass_open_channel(cycle, &ch);
} 

函数:ngx_spawn_process  
//在全局数组ngx_processes[s]中创建套接字对
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) 
{   
    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                "socketpair() failed while spawning \"%s\"", name);
    return NGX_INVALID_PID;
}
//紧跟着进行fork
pid = fork();
//这样父子进程都会共享这个全局数组
//注意在第n次执行到这里后,全局数组中的所有数据都会继承过来(重中之重,这个数组是父进程的数组)


/// 父进程都做了什么??
函数:ngx_pass_open_channel
{
    ngx_int_t  i;

    for (i = 0; i < ngx_last_process; 
        if (i == ngx_process_slot   //这样代码,表示本次创建的套接字不参与下面的流程
            || ngx_processes[i].pid == -1
            || ngx_processes[i].channel[0] == -1)
        {
            continue;
        }
        ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
                      "pass channel s:%i pid:%P fd:%d to s:%i pid:%P fd:%d",
                      ch->slot, ch->pid, ch->fd,
                      i, ngx_processes[i].pid,
                      ngx_processes[i].channel[0]);

        /* TODO: NGX_AGAIN */
        //循环遍历数组中的元素,向每个数组中的channel[0]写入ch(本次创建的套接字对,channel[0])
        ngx_write_channel(ngx_processes[i].channel[0],
                          ch, sizeof(ngx_channel_t), cycle->log);
    }
}

/// 子进程在做什么呢?
函数:ngx_worker_process_cycle
函数:ngx_worker_process_init

//初始化epoll //ngx_event_process_init
for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->init_process) {
            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
                
                /* fatal */
                exit(2);
            }
        }
}

//循环遍历,将之前数组中的channel[1]全部关闭掉
for (n = 0; n < ngx_last_process; n++) {

        if (ngx_processes[n].pid == -1) {
            continue;
        }
    
        if (n == ngx_process_slot) {
            continue;
        }
        
        if (ngx_processes[n].channel[1] == -1) {
            continue;
        }
        
        if (close(ngx_processes[n].channel[1]) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "close() channel failed");
        }
}
//把本次创建的channel[0]关闭掉
if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "close() channel failed");
}

//将本次创建的channel[1] 设置为可读状态
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                              ngx_channel_handler)
        == NGX_ERROR)
    {
        /* fatal */
        exit(2);
    }

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值