内容简介:微信公众号:关注可了解更多的关注公众号,有趣有内涵的文章第一时间送达!共享内存是linux下最基本的进程间通信方式。它通过mmap或者shmget系统调用在内存中创建一块连续的线性地址空间,使用munmap或者shmdt系统调用可以释放这块内存。使用共享内存的好处:当多个进程使用同一块共享内存时,在任何一个进程中修改了共享内存中的内容,其他进程通过访问这段共享内存都能够得到修改后的内容。
微信公众号:关注可了解更多的 Nginx
知识。任何问题或建议,请公众号留言;
关注公众号,有趣有内涵的文章第一时间送达!
共享内存
共享内存是 linux 下最基本的进程间通信方式。它通过mmap或者shmget系统调用在内存中创建一块连续的线性地址空间,使用munmap或者shmdt系统调用可以释放这块内存。使用共享内存的好处:当多个进程使用同一块共享内存时,在任何一个进程中修改了共享内存中的内容,其他进程通过访问这段共享内存都能够得到修改后的内容。
数据结构
nginx使用到的数据结构如下:
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; 复制代码
共享内存的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创建 */ 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
,是在内存中开辟了一块空间用于共享内存,而不是将硬盘中的文件映射。
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
。
1int munmap(void *addr, size_t len); 复制代码
成功:0;出错:-1
喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达
以上所述就是小编给大家介绍的《nginx共享内存分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Swoole 源码分析——内存模块之共享内存
- Swoole 源码分析——内存模块之共享内存表
- 进程间通信---共享内存
- 为什么使用通信来共享内存?
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped
- Akka系列(四):Akka中的共享内存模型
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。