NGINX 进程通信机制
nginx的進(jìn)程通信分為三種類(lèi)別:linux?系統(tǒng)與nginx?通信,?master?進(jìn)程與worker進(jìn)程通信,?worker進(jìn)程間通信。
master進(jìn)程管理worker進(jìn)程,本文將追溯nginx?的退出過(guò)程。
?
Linux信號(hào)
linux?系統(tǒng)與nginx是通過(guò)信號(hào)才進(jìn)行通信的,通過(guò)信號(hào)控制nginx重啟、關(guān)閉以及加載配置文件等。
?
信號(hào)發(fā)送流程
?
1.?發(fā)送信號(hào)
??? ./nginx –s quit? 向master進(jìn)程發(fā)送信號(hào)
?? 這里有一點(diǎn)是:執(zhí)行 ./nginx –s quit 實(shí)際上是新開(kāi)了一個(gè)master進(jìn)程,只不過(guò)它半路夭折了,即向原master發(fā)送信號(hào)后,
?? 它就死掉啦。它存在的意義就是向原master發(fā)送信號(hào)。
2.?獲取參數(shù)
??? nginx?通過(guò) -s?知道用戶(hù)要給nginx?發(fā)送信號(hào),會(huì)有相應(yīng)的動(dòng)作。?
[cpp]?view plain?copy ?print?
3.?獲取pid
?
????要發(fā)送信號(hào),需要知道m(xù)aster進(jìn)程的pid,那如何獲得呢?nginx?啟動(dòng)的時(shí)候?qū)⑵鋵?xiě)入了本地文件中。
[cpp]?view plain?copy ?print??
file.name即為配置文件中指定pid所在的文件,該文件存放master的pid。通過(guò)配置文件中的pid字段,指明存放進(jìn)程id文件的地址。
4.?發(fā)送信號(hào)??
根據(jù)name到ngx_signal_t的名字中去匹配,找到信號(hào),然后通過(guò)kill向master發(fā)送消息。
信號(hào)注冊(cè)流程
?
1. 信號(hào)定義
[cpp]?view plain?copy ?print??
簡(jiǎn)單解釋一下,signo為信號(hào)的數(shù)字表示,而signame為信號(hào)的名字,name為nginx定義的信號(hào)名,handler為回調(diào)函數(shù)。比如:
?
[cpp]?view plain?copy ?print?
轉(zhuǎn)換后為:{ SIGQUIT, ?"SIGQUIT",?"quit", ?ngx_signal_handler }, 具體語(yǔ)法規(guī)則見(jiàn)附錄。然后SIGQUIT會(huì)借助于預(yù)處理器進(jìn)行數(shù)字的對(duì)應(yīng),
SIGQUIT為在signal.h中的宏定義。
?
2. 信號(hào)注冊(cè)
[cpp]?view plain?copy ?print?signals?為ngx_signal_t?數(shù)組,內(nèi)部存放所有nginx會(huì)處理的信號(hào)。上述中,通過(guò)sigaction注冊(cè)信號(hào)。
?
3. 信號(hào)處理
[cpp]?view plain?copy ?print?設(shè)置全局變量ngx_quit 標(biāo)志為1.
?
4. 響應(yīng)信號(hào)
[cpp]?view plain?copy ?print?sigsuspend將阻塞進(jìn)程,直到set中的信號(hào)到達(dá)為止。當(dāng)有信號(hào)達(dá)到,調(diào)用handler函數(shù),然后ngx_quit被設(shè)置為1,
此時(shí),通過(guò)ngx_signal_worker_processes 向工作進(jìn)程傳遞信號(hào)。
Socket通信
master進(jìn)程與worker進(jìn)程通過(guò)sockpair進(jìn)行通信。在nginx中,將這種通信定義為頻道,channel。
master就是通過(guò)頻道與worker進(jìn)程通信滴。
?
1. 頻道定義
[cpp]?view plain?copy ?print??
2. 注冊(cè)頻道
?
其一:創(chuàng)建頻道,其實(shí)就是socketpair,在啟動(dòng)worker進(jìn)程的時(shí)候創(chuàng)建頻道
[cpp]?view plain?copy ?print?在linux下,使用socketpair函數(shù)能夠創(chuàng)建一對(duì)套節(jié)字進(jìn)行進(jìn)程間通信(IPC)。
函數(shù)原形:
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
參數(shù)1(domain):表示協(xié)議族,在Linux下只能為AF_LOCAL或者AF_UNIX。(自從Linux 2.6.27后也支持SOCK_NONBLOCK和SOCK_CLOEXEC)
參數(shù)2(type):表示協(xié)議,可以是SOCK_STREAM或者SOCK_DGRAM。SOCK_STREAM是基于TCP的,而SOCK_DGRAM是基于UDP的
參數(shù)3(protocol):表示類(lèi)型,只能為0
參數(shù)4(sv[2]):套節(jié)字柄對(duì),該兩個(gè)句柄作用相同,均能進(jìn)行讀寫(xiě)雙向操作
返回結(jié)果: 0為創(chuàng)建成功,-1為創(chuàng)建失敗,并且errno來(lái)表明特定的錯(cuò)誤號(hào),具體錯(cuò)誤號(hào)如下所述:
? ?EAFNOSUPPORT:本機(jī)上不支持指定的address。
? ?EFAULT: 地址sv無(wú)法指向有效的進(jìn)程地址空間內(nèi)。
? ?EMFILE: 已經(jīng)達(dá)到了系統(tǒng)限制文件描述符,或者該進(jìn)程使用過(guò)量的描述符。
? ?EOPNOTSUPP:指定的協(xié)議不支持創(chuàng)建套接字對(duì)。
? ?EPROTONOSUPPORT:本機(jī)不支持指定的協(xié)議。
注意:
1、該函數(shù)只能用于UNIX域(LINUX)下。
2、只能用于有親緣關(guān)系的進(jìn)程(或線(xiàn)程)間通信。
3、所創(chuàng)建的套節(jié)字對(duì)作用是一樣的,均能夠可讀可寫(xiě)(而管道PIPE只能進(jìn)行單向讀或?qū)?#xff09;。
4、在讀的時(shí)候,管道內(nèi)必須有內(nèi)容,否則將會(huì)阻塞;簡(jiǎn)而言之,該函數(shù)是阻塞的。
?
設(shè)置非阻塞,同時(shí),將ngx_channel賦值。
其二:加入epoll等待數(shù)據(jù)到來(lái),在worker進(jìn)程初始化的時(shí)候加入
ngx_channel_handler為回調(diào)函數(shù)。
?
3. 發(fā)送消息
[cpp]?view plain?copy ?print?master進(jìn)程通過(guò)ngx_signal_woker_processes向worker進(jìn)程發(fā)送消息。
?
?
?
4. 響應(yīng)消息
[cpp]?view plain?copy ?print?worker進(jìn)程通過(guò)ngx_read_channel讀取消息,然后根據(jù)command判斷是什么消息,同時(shí)設(shè)置worker進(jìn)程的ngx_quit變量。
?
5. woker進(jìn)程受理
?
[cpp]?view plain?copy ?print??
worker內(nèi)部ngx_worker_process_cycle為一個(gè)循環(huán),處理事件,當(dāng)檢測(cè)到退出標(biāo)志后,做相應(yīng)處理
?
共享內(nèi)存
?
worker進(jìn)程間則是通過(guò)比較快速的共享內(nèi)存進(jìn)行通信。
?
1. mmap 匿名
? 即不與文件關(guān)聯(lián),不需要?jiǎng)?chuàng)建文件刪除文件,簡(jiǎn)單高效。但有可能有些系統(tǒng)不支持,所以若不支持,采用第二種方案。
?
2.mmap /dev/zero
? 與字符設(shè)備/dev/zero關(guān)聯(lián),相當(dāng)于打開(kāi)一個(gè)文件,但mac os不支持,所以采用第三種方式。
? fd = open(“/dev/zero”, O_RDWR);
? mmap(NULL, sizeof(int), PROT_READ |PROT_WRITE, MAP_SHARED, fd, 0).
?
3.shmget 古老的system v獲取
? 繁瑣,但所有系統(tǒng)都支持
?
由master進(jìn)程創(chuàng)建共享變量,worker進(jìn)程共享。nginx?解決驚群?jiǎn)栴},是通過(guò)設(shè)置互斥鎖,
只有擁有互斥鎖的工作進(jìn)程才能擔(dān)負(fù)與客戶(hù)建立連接的任務(wù),這個(gè)互斥鎖就放于共享內(nèi)存中。
另外,ngin統(tǒng)計(jì)連接數(shù),這個(gè)全局變量也放于共享內(nèi)存中,多個(gè)工作進(jìn)程可以去改寫(xiě)這個(gè)變量,
當(dāng)然,需要一些互斥機(jī)制。
?
共享內(nèi)存一個(gè)小例子:
?
[cpp]?view plain?copy ?print??
?
?
附錄
?
1. #轉(zhuǎn)換為字符
? 在define宏定義中,#代表將參數(shù)轉(zhuǎn)換成字符串, 即“ ”.
? #definevalue(n)? #n
?char *str = value(12345);? è str = “12345”
?
2. ##連接成一個(gè)token
? 在define宏定義中,兩個(gè)##表示將參數(shù)連接起來(lái)構(gòu)成一個(gè)標(biāo)示符
?#define value(n)?? val##n
? intval1 = 3;
?printf(“%d\n”, value(1));?? è val1
?
3. 字符串連接
?? //C語(yǔ)言中,“ ”表示空串?? 不準(zhǔn)確
??char *str = “123”?? “456”;
?? 等效
??char *str = “123456”;
?? 解析: 相當(dāng)“123”?? “456”?紅色是一對(duì),空串省略
?
4.?為什么worker?與master采用socketpair進(jìn)行通信
???? worker進(jìn)程有一個(gè)循環(huán),在不停的處理請(qǐng)求,怎樣接收master發(fā)送的數(shù)據(jù)呢。而socketpair為一種套接字,將它加入epoll中,通過(guò)事件模塊獲取該套接字,進(jìn)行處理。ngx_write_channel將數(shù)據(jù)寫(xiě)入channel。而在ngx_process_cycle.c->ngx_worker_process_init初始化的時(shí)候,已經(jīng)調(diào)用ngx_add_channel_event將套接字寫(xiě)入epoll中,后續(xù)向套接字寫(xiě)數(shù)據(jù),epoll能檢測(cè)到,并調(diào)用注冊(cè)的回調(diào)函數(shù)ngx_channel_handler,將相應(yīng)標(biāo)志位設(shè)1(ngx_quit)。
總結(jié)
以上是生活随笔為你收集整理的NGINX 进程通信机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: tomcat+nginx+redis实现
- 下一篇: epoll机制:epoll_create