微信公衆號:鄭爾多斯
關注可瞭解更多的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;
9} ngx_shm_t;
複製代碼
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, -1, 0);
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_alloc
和ngx_shm_free
的確是對mmap
和munmap
分別進行了封裝。並且使用了MAP_ANON
,是在內存中開闢了一塊空間用於共享內存,而不是將硬盤中的文件映射。函數
首先,咱們得知道:默認狀況下,經過fork
派生的子進程並不與其父進程共享內存區。但master
與worker
進程是父子進程啊,這該怎麼辦呢?如何讓master
進程與worker
進程共享內存區呢?
解決方法就在於mmap
的flags
參數。master
進程在調用fork
以前先指定flags
爲MAP_SHARED
來調用mmap
,此時,POSIX
是保證父進程中的內存映射關係是存留到子進程中的,父進程對共享內存所作的修改子進程能看到,反過來同樣。因此流程是:master
進程在內存中以MAP_SHARED
方式開闢一塊共享內存,並映射到本身進程地址空間中的共享內存區,而後master
調用fork
,派生子進程,子進程在本身的地址空間內也會繼承這塊共享內存區。這樣問題便解決了。源碼分析
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_ANON
或MAP_ANONYMOUS
,表示不從文件中映射,只從內存中開闢一塊連續的線性地址空間出來做爲共享內存。所以,這種狀況下fd
和offset
參數就沒意義,分別置-1
和0
便可。
爲從某一進程的地址空間中刪除一個映射關係,調用munmap
。
1int munmap(void *addr, size_t len);
複製代碼
成功:0;出錯:-1
喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達