zoukankan      html  css  js  c++  java
  • 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 函数

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

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

    1int  munmap(void *addr, size_t len);

    成功:0;出错:-1


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

    郑尔多斯郑尔多斯
  • 相关阅读:
    以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序——网络程序设计课第二次作业
    逆波兰表达式改写(C++)
    侯捷老师的C++代码:基于对象的类别之二带指针的成员函数 Mystring实现
    侯捷老师的C++代码: 基于对象之一 无指针类型 复数类实现
    雇员记录系统(C++)
    设计模式-Interpreter(行为模式) 使用解释器给用户提供一个一门定义语言的语法表示的解释器,通过该解释器解释语言中的句子。
    设计模式-Iterator(行为模式) 将聚合的遍历封装到一个类中
    设计模式-Chain of Responsibility (行为模式) 降低系统的耦合性
    设计模式-Visitor(行为模式) 一个类在不修改自己的同时增加了新的操作,存在问题是 1:破坏了封装性 2:扩展困难
    设计模式-Command(行为模式) 将一个请求封装到一个Command类中,提供一个处理对象Receiver,将Command由Invoker激活。
  • 原文地址:https://www.cnblogs.com/zhengerduosi/p/10362553.html
Copyright © 2011-2022 走看看