微信公众号:郑尔多斯
关注可了解更多的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
喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达