内容简介:微信公众号:关注可了解更多的关注公众号,有趣有内涵的文章第一时间送达!共享内存是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共享内存分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Sovereign Individual
James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00
Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!