社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  NGINX

nginx共享内存分析

郑尔多斯 • 6 年前 • 559 次点击  
阅读 26

nginx共享内存分析

微信公众号:郑尔多斯
关注可了解更多的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;   
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,三种方式分别如下:
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, -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 函数

1


    
void *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_ANONMAP_ANONYMOUS,表示不从文件中映射,只从内存中开辟一块连续的线性地址空间出来作为共享内存。因此,这种情况下fdoffset参数就没意义,分别置-10即可。

为从某一进程的地址空间中删除一个映射关系,调用munmap

1int  munmap(void *addr, size_t len);
复制代码

成功:0;出错:-1


喜欢本文的朋友们,欢迎长按下图关注订阅号郑尔多斯,更多精彩内容第一时间送达

郑尔多斯
郑尔多斯

今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/gtifdXIv2F
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/28719
 
559 次点击