Nginx的master和worker進程間的通信
前面單獨分析了master進程和worker的工作情況,本文就大概看一下master進程和worker進程之間是如何使用channel來完成通信的。這部分實現的源碼主要分布於src/os/unix/channel.h和channel.c兩個文件中。實現極其簡單,沒有什麼複雜的邏輯。下面,我繪製了一個簡單的master進程和worker進程間的關係,圖中的箭頭符號指出數據是由master進程傳給worker進程,而沒有從worker到master;這是因為channel不是一個普通的數據傳輸管道,在Nginx中它僅僅是用著master發送指令給worker的一個管道,master藉此channel來告訴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;
這個結構中的4個欄位分別是發送的指令、worker進程的pid、worker進程的slot(在ngx_proecsses中的索引)及一個文件描述符。master進程可能會將一個打開的文件描述符發送給worker進程進行讀寫操作,那麼此時就需要填寫fd這個欄位了。worker進程在收到一個這樣的結構數據後,通過判斷command的值來採取相應的動作;command就是master給worker下達的命令。
master進程用於處理SIGCHLD信號的函數ngx_reap_children中就有向worker進程發送關閉channel的指令,我們看看這個例子是怎麼做的。
ch.command = NGX_CMD_CLOSE_CHANNEL;
ch.fd = -1;
ch.pid = ngx_processes[i].pid;
ch.slot = i;
ngx_write_channel(ngx_processes[n].channel[0],
&ch, sizeof(ngx_channel_t), cycle->log);
這幾行代碼是我從ngx_reap_children函數中拼湊起來的,所以看上去好像有點奇怪,不那麼順暢;但卻清晰的給我們展現了master進程怎麼給一個worker進程發送指令,此處發送的指令時NGX_CMD_CLOSE_CHANNEL。發送指令的函數ngx_write_channel是利用sendmsg來完成,《Unix網路編程》可以詳細了解sendmsg。
worker進程在調用ngx_worker_process_init進行初始化的時候,使用了如下兩行代碼將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所做的事情。
/*
讀出master進程發送給過來的指令數據, ngx_read_channel
是利用recvmsg實現,詳細介紹見《unix網路編程》
*/
n = ngx_read_channel(c->fd, &ch,
sizeof(ngx_channel_t), ev->log);
/*
判斷command的值,從而採取具體的動作,
代碼意圖都寫得很明顯,
就不在這裡多說了。
*/
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_processes[ch.slot].pid = ch.pid;
ngx_processes[ch.slot].channel[0] = ch.fd;
break;
case NGX_CMD_CLOSE_CHANNEL:
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;
}
Nginx中關於整個channel的實現就這麼簡單,沒有什麼多餘的事情。
技術交流Q群:
聊聊技術+妹紙。


※Nginx 架構初探
※zendAPI:讓 PHP 的擴展開發成為一種享受
※php的垃圾回收機制——引用計數
※colly-go語言編寫的CPU單核超過1k次請求的web採集利器
※MultiHttp:高性能的 PHP 封裝的 HTTP Restful 多線程並發請求庫
TAG:PHP技術大全 |