nginx 進程通信--共享內存


共享內存是Linux下進程之間進行數據通信的最有效方式之一,而nginx就為我們提供了統一的操作接口來使用共享內存。

在nginx里,一塊完整的內存以結構體ngx_shm_zone_s封裝.其中包括是共享內存的名字(shm_zone[i].shm.name),大小(shm_zone[i].shm.size),標簽(shm_zone[i].tag),      ngx_shm_zone_init_pt      init;  (初始化共享內存時的回調函數)

 一些共享內存的結構體:

struct ngx_shm_zone_s
    void                     *data;       
    ngx_shm_t                 shm;
    ngx_shm_zone_init_pt      init;
    void                     *tag;
};
typedef struct {
    u_char      *addr;
    size_t       size;   
    ngx_str_t    name; //名字
    ngx_log_t   *log;
    ngx_uint_t   exists;   /* unsigned  exists:1;  */
} ngx_shm_t;

      這些字段大都容易理解,只有tag字段需要解釋一下,因為看上去它和name字段有點重復,而事實上,name字段主要用作共享內存的唯一標識,它能讓nginx知道我想使用哪個共享內存,但它沒法讓nginx區分我到底是想新創建一個共享內存,還是使用那個已存在的舊的共享內存。舉個例子,模塊A創建了共享內存sa,模塊A或另外一個模塊B再以同樣的名稱sa去獲取共享內存,那么此時nginx是返回模塊A已創建的那個共享內存sa給模塊A/模塊B,還是直接以共享內存名重復提示模塊A/模塊B出錯呢?不管nginx采用哪種做法都有另外一種情況出錯,所以新增一個tag字段做沖突標識,該字段一般也就指向當前模塊的ngx_module_t變量即可。這樣在上面的例子中,通過tag字段的幫助,如果模塊A/模塊B再以同樣的名稱sa去獲取模塊A已創建的共享內存sa,模塊A將獲得它之前創建的共享內存的引用(因為模塊A前后兩次請求的tag相同),而模塊B則將獲得共享內存已做它用的錯誤提示(因為模塊B請求的tag與之前模塊A請求時的tag不同)。

     當我們要使用一個共享內存時,總會在配置文件里加上該共享內存的相關配置信息,而nginx在進行配置解析的過程中,根據這些配置信息就會創建相應的共享內存,不過此時的創建只是代表共享內存的結構體變量的ngx_shm_zone_t的創建,具體實現在share_memory_add()中.nginx中所有共享內存都是以list鏈表的形式組織在全局變量cf->cycle->shared_memory下,在創建新的共享內存之前會先對該鏈表進行遍歷查找以及沖突檢測,對於已經存在且不存在沖突的共享內存可直接返回引用。

以nginx中ngx_http_limit_req_module模塊為例://nginx限制鏈接

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;  

rate=1r/s 的意思是每個地址每秒只能請求一次,原理 burst=120 一共有120塊令牌,並且每秒鍾只新增1塊令牌,120塊令牌發完后 多出來的那些請求就會返回503

nginx在進行配置解析的時,遇到limit_req_zone配置項時,調用ngx_http_limit_req_zone(),而在該函數中繼續調用share_memory_add()創建ngx_shm_zone_t結構體變量並加入到全局鏈表中:ngx_http_limit_req_zone() -> ngx_shared_memory_add() -> ngx_list_push()。

共享內存的真正創建是在配置文件全部解析完后,所有代表共享內存的結構體ngx_shm_zone_t變量以鏈表的形式掛接在全局變量cf->cycle->shared_memory下,nginx此時遍歷該鏈表並逐個進行實際創建,即分配內存、管理機制(比如鎖、slab)初始化等:

ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{ 
        part = &cycle->shared_memory.part;
        shm_zone = part->elts;
        ...............
        for (i = 0; /* void */ ; i++) {
              if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
            goto failed;
        }

        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
            goto failed;
        }

        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
            goto failed;
        }
} 

     其中函數ngx_shm_alloc()時共享內存的實際分配,針對當前系統可提供的接口,可以時mmap,shmget等,而ngx_init_zone_pool()函數是共享內存管理機制的初始化,共享內存涉及2個主題:(1)多進程共同使用共享內存塊,必須考慮互斥問題 (2)nginx以性能著稱,那么對於共享內存自然也有其獨特的使用方式,雖然我們可以不用,但在這里也默認都會以這種slab的高效訪問機制進行初始化.

     回調函數shm_zone[i].init()是各個共享內存所特定的,根據使用方的自身需求不同而不同,這也是我們在使用共享內存時需特別注意的函數.

     

      函數ngx_http_limit_req_init_zone()的第二個參數data表示‘舊’數據,在進行重新加載配置時(即nginx收到SIGHUP信號)該值將不為空,如果舊數據可繼續使用,那么可直接返回NGX_OK;否則,需根據自身模塊邏輯對共享內存的使用做相關初始化,比如ngx_http_limit_req_module模塊,在第634、642行直接使用默認已初始化好的slab機制,進行內存的分配等。當函數ngx_http_limit_req_init_zone()正確執行結束,一個完整的共享內存就已創建並初始完成,接着要做的就是共享內存的使用,這即回到前面提到的兩個主題:互斥與slab。

函數

含義

ngx_shmtx_create()

創建

ngx_shmtx_destory()

銷毀

ngx_shmtx_trylock()

嘗試加鎖(加鎖失敗則直接返回,不等待)

ngx_shmtx_lock()

加鎖(持續等待,直到加鎖成功)

ngx_shmtx_unlock()

解鎖

ngx_shmtx_force_unlock()

強制解鎖(可對其它進程進行解鎖)

ngx_shmtx_wakeup()

喚醒等待加鎖進程(系統支持信號量的情況下才可用)

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM