本文 主要分析master进程和worker进程之间是如何使用channel来完成通信的。这部分实现的代码主要分布在src/os/unix/ngx_channel.c文件中。master进程将数据传给worker进程,用socketpair创建的通道channel[0]将命令发送给worker进程,告诉worker进程要做什么。而worker进程不需要告诉master进程做什么,是一个单向的通道。
master进程每次发送给worker进程的指令用如下的一个结构来完成封装
typedef struct {
ngx_uint_t command;
ngx_pid_t pid;
ngx_int_t slot;
ngx_fd_t fd;
} ngx_channel_t;
四个成员分别表示命令、worker进程的pid、worker进程的slot(在ngx_processes数组中的下标)、文件描述符。
worker进程在收到这个的一个结构数据后,通过判断command来作相应的动作。
master进程用于处理SIGCHLD信号的函数ngx_reap_children中就有向worker进程发送关闭channel的指令
worker进程在调用ngx_worker_process_init函数时,通过ngx_add_channel_event将channel放到epoll事件处理模块中。
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
ngx_channel_handler)
== NGX_ERROR)
{
/* fatal */
exit(2);
}
当收到master进程发过来的命令后,就调用ngx_channel_handler处理,ngx_channel_handler主要做什么呢
switch (ch.command) {
case NGX_CMD_QUIT:
ngx_quit = 1;
break;
case NGX_CMD_TERMINATE:
ngx_terminate = 1;
break;
case NGX_CMD_REOPEN:
ngx_reopen = 1;
break;
case NGX_CMD_OPEN_CHANNEL:
ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
"get channel s:%i pid:%P fd:%d",
ch.slot, ch.pid, ch.fd);
ngx_processes[ch.slot].pid = ch.pid;
ngx_processes[ch.slot].channel[0] = ch.fd;
break;
case NGX_CMD_CLOSE_CHANNEL:
ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
"close channel s:%i pid:%P our:%P fd:%d",
ch.slot, ch.pid, ngx_processes[ch.slot].pid,
ngx_processes[ch.slot].channel[0]);
if (close(ngx_processes[ch.slot].channel[0]) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
"close() channel failed");
}
ngx_processes[ch.slot].channel[0] = -1;
break;
}
根据收到的命令类型,采取相应的动作。
整个通信的过程就是如此