從nginx角度看伺服器多程序模型設計 一

2021-08-26 22:34:36 字數 3590 閱讀 6618

多程序你可能很熟悉,也許有一套自己的使用習慣和方法。這東西沒有什麼權威建議,書上只是給出了基本知識點,至於具體怎麼去用,因人而異。nginx在多程序設計方面有很多值得學習和借鑑的東西,我認為是一套比較好的實現方案。你也許認為這東西很簡單,是老生常談的東西了,但是我這裡要提醒你一下,俗話道酒是陳的香,越經典的東西越值得去琢磨,不要對自己太自信。善於思考的傢伙總是會在一些老的技術上給你許多新鮮的見解,這種牛人你不會沒遇到過吧!扯淡罷了,回到正題。

看函式ngx_spawn_process的骨架:

for (s = 0; s < ngx_last_process; s++) 

.../*

* 實現非同步通知的兩個步驟,在nginx事件模型的rtsig機制中會用到

* 參考:

*/if (ioctl(ngx_processes[s].channel[0], fioasync, &on) == -1)

if (fcntl(ngx_processes[s].channel[0], f_setown, ngx_pid) == -1)

...ngx_channel = ngx_processes[s].channel[1];

...// 當前產生的子程序在ngx_processes陣列中的下標

ngx_process_slot = s;

/* * 在fork之後,父程序的ngx_processes陣列,「傳遞」給了子程序,但是這時子程序拿到的陣列是截至建立該程序之前其他程序的資訊。

* 由於子程序是父程序fork得到的,那麼在之後父程序的操作結果在子程序中就不可見了。假設當前誕生的是程序1,用p1表示,當父程序

* 建立p5時,那麼p2-p5的程序資訊在p1中是缺失的,那麼p1需要這些資訊嗎?如果需要的話,該通過什麼手段給它呢?

* 見ngx_start_worker_processes相關分析

*/ngx_pid = fork();

switch (pid)

...ngx_processes[s].pid = pid;

...if (s == ngx_last_process)

/* * 父程序,也就是master程序,依次建立work子程序,為了確保ngx_processes陣列在子程序間同步,每次建立完乙個子程序,

* 就通過ngx_pass_open_channel,做一次廣播,告訴先前已經建立的子程序: "新程序誕生,注意更新程序陣列相關項"

* 那麼這些子程序又是如何更新這些資訊的呢?答案在ngx_worker_process_init中。

*/ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)

}

在worker程序的程序資訊陣列中,把當前worker程序資訊結構(即ngx_process_slot對應的位置)中的channel[0]關掉,

同時會把其他程序的channel[1]關掉,而只把他們的channel[0]留著,如下**所示。那麼這樣做的意圖是什麼?

ngx_worker_process_init(ngx_cycle_t *cycle, ngx_uint_t priority) 

if (n == ngx_process_slot)

if (ngx_processes[n].channel[1] == -1)

if (close(ngx_processes[n].channel[1]) == -1)

}if (close(ngx_processes[ngx_process_slot].channel[0]) == -1)

}

在ngx_worker_process_init的最後,呼叫了函式ngx_add_channel_event:

ngx_add_channel_event(cycle, ngx_channel, ngx_read_event,ngx_channel_handler)

主要的目的是向epoll中註冊讀事件。讀資料的fd為變數ngx_channel,處理函式是ngx_channel_handler,那麼ngx_channel是什麼, 這個處理函式又做了些什麼呢?

在ngx_spawn_process函式中,有句:ngx_channel = ngx_processes[s].channel[1],我們知道s是本次用來放置新建立程序資訊的(在ngx_processes中)位置,即也是ngx_process_slot變數,因為這兩個變數,ngx_channel和ngx_process_slot是在fork之前設定的,那麼在隨後子程序的init時,就會用到這兩個剛剛在父程序中設定的值,ngx_add_channel_event中的ngx_channel就是當前子程序的channle[1],所以這裡的意圖也就明了了,該worker程序就是通過監聽channel[1]的讀事件來獲取資訊。好了,然後我們搜遍所有**,並沒有看到在某個channel上監聽寫事件的動作,那麼我們要問了,子程序有沒有需要寫一些資料的時候呢?好吧,我們全文搜尋"channel[0]",看看會發現什麼。我想你肯定發現了ngx_write_channel函式,它的作用就是往channel[0]中去寫資料,那麼是都是誰在寫呢?

我們找到了ngx_pass_open_channel,也就是我上文所說的「廣播」。你要知道的一點就是,父程序中通過向channle[0]裡寫資料,可以在子程序中相應的channel[1]中讀取。好了到這裡我們前面提到的一些疑問基本上都有了答案,父程序通過向各個channel[0]中「廣播」資料,子程序在其自己的channel[1]中讀取相應的資料,他們正是通過這種方式來通訊的。那麼nginx程序間通訊的機制僅僅就是這些嗎?遠不止。。。

我們前面提到父程序寫channel[0],子程序讀channel[1],那麼父程序都傳了些啥?我們最容易看到的載體是ngx_channel_t結構,在ngx_pass_open_channel中,引數ch就是這樣的乙個結構:

/*

* command傳遞的命令

* pid本次產生的新程序pid

* slot新程序在ngx_processes陣列中的位置

* fd新程序中channel[0]

*/typedef struct ngx_channel_t;

對於command,在當前為ngx_cmd_open_channel,也就是告訴其他的子程序,「某個新的程序剛剛誕生,注意同步相關資訊」。那麼子程序得到這個資訊會怎麼做呢?

還記得這個ngx_channel_handler函式嗎,在註冊讀事件時設定的,它裡面會呼叫ngx_read_channel來或者這個channel資料。如果發現是ngx_cmd_open_channel命令,那麼就在ngx_processes的相應位置上更新新誕生程序的資訊:

ngx_processes[ch.slot].pid = ch.pid;

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

這個ngx_cmd_open_channel算是最簡單的命令了,其他的處理有什麼特別的地方?

以上我們討論的這些算是基礎設施了,在這之上,nginx做了很多的好東西。目前看到的只是在nginx初始化時做的事情,那麼在實際執行中,程序間又有哪些互動和通訊呢?後面的文章討論。一篇討**章太長的話,大家都蛋疼,你懂得。。。

從nginx角度看伺服器多程序模型設計 二

在ngx master process cycle中要處理眾多的全域性變數,正是通過一些訊號處理函式設定這些變數,才會後面檢測到一些事件的發生。我們來看看都有哪些預定義的事件,以及他們是如何被處理的。多數的事件來自於nginx的使用者,他們可能終止nginx,重啟,重讀配置等等,這些操作則主要依賴於...

多程序伺服器

基於tcp實現多程序伺服器 伺服器端 1 建立套接字 include include int socket int domain,int type,int protocol domain 乙個位址描述。目前僅支援af inet格式,也就是說arpa internet位址格式。type 指定socke...

多程序伺服器

注意 包含了 wrap.c 和 wrap.h 檔案在上篇部落格中 server.c include include include in.h include include include include include include wrap.h define maxline 8192 defi...