在nginx的進程模型下,相似流量統計、流量控制、數據共享、等須要多個工做進程共同配合完成任務,共享內存是一個重要的進程通信的方案。本文介紹在nginx的代碼中與共享內存相關的功能,包括ngx_shmem與ngx_slab的使用與注意事項,但不包括ngx_slab中實現的內存管理算法。html
ngx_shmem.c/h文件只是對mmap()/munmap()系統調用或者shmget()/shmdt()的一個很簡單的封裝。實現了ngx風格的基礎庫,能夠申請和釋放一段連續的共享內存空間。通常用於固定長度的共享數據使用,使用過程當中數據長度固定不會伸縮。nginx
typedef struct { u_char *addr; size_t size; ... } ngx_shm_t; ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); void ngx_shm_free(ngx_shm_t *shm);
在ngxin中共享內存的使用流程,通常是由master進程建立,worker進程經過繼承的方式得到內存指針。算法
關於ngx_shmem的使用,能夠參考ngx_event_module_init()中部分片斷,這部分代碼在共享內存中建立了若干個變量,用於記錄各個狀態(accepted/reading/writing...)的請求數量,並在ngx_event_module中的幾個關鍵事件入口對這幾個變量進行加減統計操做。實現統計全部worker進程當前的請求狀態。數據結構
shm.size = size; ngx_str_set(&shm.name, "nginx_shared_zone"); shm.log = cycle->log; if (ngx_shm_alloc(&shm) != NGX_OK) { return NGX_ERROR; } shared = shm.addr; ... ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl); ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl); ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl); ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl); ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl); ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl); ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
關於這個功能的更多細節,能夠查看代碼中的NGX_STAT_STUB宏定義相關代碼與ngx_http_stub_status_module。併發
ngx_shmem是一層極簡的封裝,實現了共享內存的基本功能。但咱們程序中大部分的場景共享數據並不會一個固定大小的結構,而更可能是像ngx_array、ngx_list、ngx_queue、ngx_rbtree這類大小能夠變化的數據結構。框架
咱們指望能有像ngx_pool_t同樣能夠動態申請釋放空間一個內存池。ngx_slab正是一個這樣的結構體,原理上與系統的malloc()有相識之處都是經過一系列算法實現對一段段內存片斷的申請與釋放。只不過ngx_slab操做的對象是基於ngx_shmem的共享內存。函數
先看一下ngx_slab的接口ui
typedef struct { ngx_shmtx_t mutex; ... void *data; /* 通常存放從pool中申請得到的根數據地址(pool中第一個申請的數據接口) */ void *addr; /* 使用ngx_shmem申請得到的共享內存基地址 */ } ngx_slab_pool_t; void ngx_slab_init(ngx_slab_pool_t *pool); void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size); void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size); void ngx_slab_free(ngx_slab_pool_t *pool, void *p); void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);
能夠看到接口並不複雜,alloc與calloc的區別在因而否對申請得到的內存段清零,_locked結尾的接口表示操做的pool已是獲取到鎖的。在ngx_slab_pool_t的結構體有一個ngx_shmtx_t的互斥鎖用於同步多進程同時訪問pool的併發場景。注意ngx_slab_alloc()會先獲取鎖、而後申請空間、最後釋放鎖。而ngx_slab_alloc_locked()則直接申請空間,認爲程序已經在其餘邏輯中得到鎖了。atom
在nginx的開發中使用ngx_shmem通常須要遵循如下初始化流程:指針
在這個流程中,涉及到ngx_shared_memory_add()接口與對應的ngx_shm_zone_t結構體。
struct ngx_shm_zone_s { void *data; ngx_shm_t shm; ngx_shm_zone_init_pt init; void *tag; void *sync; ngx_uint_t noreuse; /* unsigned noreuse:1; */ }; ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag);
其中值得一提的是noreuse屬性,這個屬性控制了在nginx的reload過程當中是否會從新申請共享內存。
因爲關於ngx_init_cycle()函數較長,這個流程能夠經過查找/* create shared memory */這個註釋或者cycle->shared_memory這個對象查看相關代碼。
關於ngx_slab更多細節的使用,建議能夠參考ngx_http_limit_conn_module,這是經過共享內存實現鏈接數限制的模塊,模塊複雜度底,是一個很好的參考範例。
同時安利一波《深刻理解Nginx》做者 陶輝 在極客時間出版的《Nginx核心知識100講》,近期618彷佛有打折活動,經過我分享的連接進行購買,我也將得到部分返現,感謝支持。