nginx共享記憶體分析
微信公眾號:關注可瞭解更多的 Nginx
知識。任何問題或建議,請公眾號留言;
關注公眾號,有趣有內涵的文章第一時間送達!
共享記憶體
共享記憶體是linux下最基本的程序間通訊方式。它通過mmap或者shmget系統呼叫在記憶體中建立一塊連續的線性地址空間,使用munmap或者shmdt系統呼叫可以釋放這塊記憶體。使用共享記憶體的好處:當多個程序使用同一塊共享記憶體時,在任何一個程序中修改了共享記憶體中的內容,其他程序通過訪問這段共享記憶體都能夠得到修改後的內容。
資料結構
nginx使用到的資料結構如下:
1 typedef struct { 2u_char*addr;/* 共享記憶體的起始地址 */ 3size_tsize;/* 共享記憶體的長度 */ 4ngx_str_tname;/* 共享記憶體的名字*/ 5ngx_log_t*log;/* 記錄日誌的物件 */ 6 7/* unsignedexists:1;*/ /*共享記憶體是否已經分配過,1:已經分配 */ 8ngx_uint_texists; 9} ngx_shm_t; 複製程式碼
共享記憶體的API
nginx
操作共享記憶體的 API
有兩個,如下:
1ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);/* 分配新的共享記憶體 */ 2void ngx_shm_free(ngx_shm_t *shm);/*釋放已經存在的共享記憶體 */ 複製程式碼
實現方式
上面是 nginx
中操作共享記憶體的兩個 api
。我們應該使用上述兩個 api
對共享記憶體進行操作。
為了可移植性, nginx
使用了三種方式來實現上述的兩個 api
,三種方式分別如下:
1、不對映檔案使用 mmap
分配共享記憶體
2、以 /dev/zero
檔案使用 mmap
對映共享記憶體。
3、用 shmget
呼叫來分配共享記憶體
原始碼分析
原始碼很簡單,我們對 mmap
實現方式進行簡單的分析
1#if (NGX_HAVE_MAP_ANON) 2 3ngx_int_t 4ngx_shm_alloc(ngx_shm_t *shm) 5{ 6/* MAP_ANON:不使用檔案對映方式,因此fd,offset無用,相當於在記憶體開闢一塊空間用於共享,由master建立 */ 7shm->addr = (u_char *) mmap(NULL, shm->size, 8PROT_READ|PROT_WRITE, 9MAP_ANON|MAP_SHARED, -1, 0); 10 11if (shm->addr == MAP_FAILED) { 12ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, 13"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size); 14return NGX_ERROR; 15} 16 17return NGX_OK; 18} 19 20 21void 22ngx_shm_free(ngx_shm_t *shm) 23{ 24if (munmap((void *) shm->addr, shm->size) == -1) { 25ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, 26"munmap(%p, %uz) failed", shm->addr, shm->size); 27} 28} 29 30#elif (NGX_HAVE_MAP_DEVZERO) 複製程式碼
可以看到 ngx_shm_alloc
和 ngx_shm_free
的確是對 mmap
和 munmap
分別進行了封裝。而且使用了 MAP_ANON
,是在記憶體中開闢了一塊空間用於共享記憶體,而不是將硬碟中的檔案對映。
nginx如何使用共享記憶體
首先,我們得知道:預設情況下,通過 fork
派生的子程序並不與其父程序共享記憶體區。但 master
與 worker
程序是父子程序啊,這該怎麼辦呢?如何讓 master
程序與 worker
程序共享記憶體區呢?
解決方法就在於 mmap
的 flags
引數。 master
程序在呼叫 fork
之前先指定 flags
為 MAP_SHARED
來呼叫 mmap
,此時, POSIX
是保證父程序中的記憶體對映關係是存留到子程序中的,父程序對共享記憶體所做的修改子程序能看到,反過來一樣。所以流程是: master
程序在記憶體中以 MAP_SHARED
方式開闢一塊共享記憶體,並對映到自己程序地址空間中的共享記憶體區,然後 master
呼叫 fork
,派生子程序,子程序在自己的地址空間內也會繼承這塊共享記憶體區。這樣問題便解決了。
mmap/munmap 函式
1void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); 複製程式碼
MAP_FAILED
addr
: 指定的fd描述符應被對映到程序地址空間的起始地址,一般為 NULL
,意思就是讓核心自己去選擇起始地址
len
: 對映到程序地址空間的位元組數
prot
:對這塊共享記憶體中的資料,我們可以處理的方式,如下:
prot | 說明 |
---|---|
PROT_READ | 資料可讀 |
PROT_WRITE | 資料可寫 |
PROT_EXEC | 資料可執行 |
PROT_NONE | 資料不可訪問 |
flags
:變動共享記憶體區中的資料這一行為是共享的還是私有的,即對所有程序可見,還是隻對該程序可見。如下:
flags | 說明 |
---|---|
MAP_SHARED | 變動是共享的 |
MAP_PRIVATE | 變動是私有的 |
MAP_FIXED | 準確的解釋addr引數 |
fd
:被對映的檔案描述符
offset
:被對映區域在檔案中的起始位置。
具體的見下圖:

需要注意的是: nginx
的共享記憶體不是對映檔案中的內容。當 flags
引數中 MAP_ANON
或 MAP_ANONYMOUS
,表示不從檔案中對映,只從記憶體中開闢一塊連續的線性地址空間出來作為共享記憶體。因此,這種情況下 fd
和 offset
引數就沒意義,分別置 -1
和 0
即可。
為從某一程序的地址空間中刪除一個對映關係,呼叫 munmap
。
1intmunmap(void *addr, size_t len); 複製程式碼
成功:0;出錯:-1
喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達
