nginx共享內存分析

微信公衆號:鄭爾多斯
關注可瞭解更多的Nginx知識。任何問題或建議,請公衆號留言;
關注公衆號,有趣有內涵的文章第一時間送達!linux

共享內存

共享內存是linux下最基本的進程間通訊方式。它經過mmap或者shmget系統調用在內存中建立一塊連續的線性地址空間,使用munmap或者shmdt系統調用能夠釋放這塊內存。使用共享內存的好處:當多個進程使用同一塊共享內存時,在任何一個進程中修改了共享內存中的內容,其餘進程經過訪問這段共享內存都可以獲得修改後的內容。nginx

數據結構

nginx使用到的數據結構以下:api

1 typedef struct {
2    u_char      *addr;    /* 共享內存的起始地址 */
3    size_t       size;    /* 共享內存的長度 */
4    ngx_str_t    name;    /* 共享內存的名字  */
5    ngx_log_t   *log;     /* 記錄日誌的對象 */
6
7/* unsigned  exists:1;  */ /*共享內存是否已經分配過,1:已經分配 */
8    ngx_uint_t   exists;   
9ngx_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,三種方式分別以下:
一、不映射文件使用mmap分配共享內存
二、以 /dev/zero 文件使用mmap映射共享內存。
三、用shmget調用來分配共享內存數據結構

源碼分析

源碼很簡單,咱們對mmap實現方式進行簡單的分析app

 1#if (NGX_HAVE_MAP_ANON)
2
3ngx_int_t
4ngx_shm_alloc(ngx_shm_t *shm)
5{
6    /* MAP_ANON:不使用文件映射方式,所以fd,offset無用,至關於在內存開闢一塊空間用於共享,由master建立 */
7    shm->addr = (u_char *) mmap(NULL, shm->size,
8                                PROT_READ|PROT_WRITE,
9                                MAP_ANON|MAP_SHARED, -10);
10
11    if (shm->addr == MAP_FAILED) {
12        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
13                      "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
14        return NGX_ERROR;
15    }
16
17    return NGX_OK;
18}
19
20
21void
22ngx_shm_free(ngx_shm_t *shm)
23
{
24    if (munmap((void *) shm->addr, shm->size) == -1) {
25        ngx_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_allocngx_shm_free的確是對mmapmunmap分別進行了封裝。並且使用了MAP_ANON,是在內存中開闢了一塊空間用於共享內存,而不是將硬盤中的文件映射。函數

nginx如何使用共享內存

首先,咱們得知道:默認狀況下,經過fork派生的子進程並不與其父進程共享內存區。但masterworker進程是父子進程啊,這該怎麼辦呢?如何讓master進程與worker進程共享內存區呢?
解決方法就在於mmapflags參數。master進程在調用fork以前先指定flagsMAP_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:對這塊共享內存中的數據,咱們能夠處理的方式,以下:ui

prot 說明
PROT_READ 數據可讀
PROT_WRITE 數據可寫
PROT_EXEC 數據可執行
PROT_NONE 數據不可訪問

flags:變更共享內存區中的數據這一行爲是共享的仍是私有的,即對全部進程可見,仍是隻對該進程可見。以下:spa

flags 說明
MAP_SHARED 變更是共享的
MAP_PRIVATE 變更是私有的
MAP_FIXED 準確的解釋addr參數

fd:被映射的文件描述符
offset:被映射區域在文件中的起始位置。
具體的見下圖:

映射區域
映射區域

須要注意的是:nginx的共享內存不是映射文件中的內容。當flags參數中MAP_ANONMAP_ANONYMOUS,表示不從文件中映射,只從內存中開闢一塊連續的線性地址空間出來做爲共享內存。所以,這種狀況下fdoffset參數就沒意義,分別置-10便可。

爲從某一進程的地址空間中刪除一個映射關係,調用munmap

1int  munmap(void *addr, size_t len);
複製代碼

成功:0;出錯:-1


喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯
相關文章
相關標籤/搜索