nginx中的共享內存如何使用

ngx_shmem的使用

ngx_shmem.c/h文件只是對mmap()/munmap()系統調用或者shmget()/shmdt()的一個很簡單的封裝。實現了ngx風格的基礎庫,可以申請和釋放一段連續的共享內存空間。一般用于固定長度的共享數據使用,使用過程中數據長度固定不會伸縮。

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_slab的使用

ngx_shmem是一層極簡的封裝,實現了共享內存的基本功能。但我們程序中大部分的場景共享數據并不會一個固定大小的結構,而更多是像ngx_array、ngx_list、ngx_queue、ngx_rbtree這類大小可以變化的數據結構。

我們期望能有像ngx_pool_t一樣可以動態申請釋放空間一個內存池。ngx_slab正是一個這樣的結構體,原理上與系統的malloc()有相識之處都是通過一系列算法實現對一段段內存片段的申請與釋放。只不過ngx_slab操作的對象是基于ngx_shmem的共享內存。

先看一下ngx_slab的接口

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()則直接申請空間,認為程序已經在其他邏輯中獲得鎖了。

nginx的開發中使用ngx_shmem一般需要遵循以下初始化流程:

  • 模塊在配置解析過程中調用ngx_shared_memory_add()接口,注冊一段共享內存。提供共享內存大小與內存初始化的回調函數。

  • 框架在ngx_init_cycle()中使用ngx_shmem申請內存,并初始化ngx_slab,然后回調模塊注冊的初始化函數

  • 模塊使用ngx_slab的申請/是否接口

在這個流程中,涉及到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這個對象查看相關代碼。

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享