Nginx源码阅读:共享内存ngx_shm_t和它的组织方式ngx_shm_zone_t、ngx_list_t

简介: Nginx源码阅读:共享内存ngx_shm_t和它的组织方式ngx_shm_zone_t、ngx_list_t

一、Nginx中共享内存的结构图

一个ngx_list_t来组织所有的ngx_shm_zone_t

一个ngx_shm_zone_t来管理一块共享内存

二、Nginx中实现共享内存的部分

1、共享内存配置信息的结构体ngx_shm_t

该结构体并非创建于共享内存空间中,而是通过内存池去创建这个结构体,通过这个结构体(共享内存的配置信息),调用ngx_shm_alloc传入共享内存的配置信息ngx_shm_t,从而创建一块共享内存空间。该结构体的addr就是指向共享内存的首地址

typedef struct {
    u_char      *addr;//共享内存的起始地址
    size_t       size;//共享内存的大小(字节)
    ngx_str_t    name;
    ngx_log_t   *log;
    ngx_uint_t   exists;   /* unsigned  exists:1;  */
} ngx_shm_t;

主要实现两个函数,分配共享内存和释放共享内存

ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
void ngx_shm_free(ngx_shm_t *shm);

2、分配共享内存ngx_shm_alloc

通过mmap实现,获取一块共享内存的空间,并将共享内存的首地址用ngx_shm_taddr去接受

ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
    shm->addr = (u_char *) mmap(NULL, shm->size,
                                PROT_READ|PROT_WRITE,
                                MAP_ANON|MAP_SHARED, -1, 0);
    if (shm->addr == MAP_FAILED) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
        return NGX_ERROR;
    }
    return NGX_OK;
}

3、释放共享内存ngx_shm_free

通过munmap实现

void
ngx_shm_free(ngx_shm_t *shm)
{
    if (munmap((void *) shm->addr, shm->size) == -1) {
        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
                      "munmap(%p, %uz) failed", shm->addr, shm->size);
    }
}

三、管理共享内存(通过数据结构将它组织起来)

1、ngx_shm_zone_t

ngx_shm_t是对共享内存配置信息的结构体,那么ngx_shm_zone_t是包含用户数据信息的共享内存配置的结构体(换句话说,就是ngx_shm_t的基础上,引入了用户数据,包括如何初始化这块数据等操作)

同样,这个是(引入了用户信息)共享内存的配置信息,是通过内存池创建的。

struct ngx_shm_zone_s {
    void                     *data;//用户数据
    ngx_shm_t                 shm;//共享内存基本的结构体
    ngx_shm_zone_init_pt      init;//用户数据的初始化函数(用于初始化shm这个结构体,但真正的共享内存并不在这创建)
    void                     *tag;//创建的共享内存模块
    void                     *sync;
    ngx_uint_t                noreuse;  /* unsigned  noreuse:1; */
};

data数据是作为init初始化函数的参数。

2、ngx_list_t

ngx_list_t就是用来管理ngx_shm_zone_t

ngx_list_t中每隔chunck,分为n等分,每一块的大小是ngx_shm_zone_t所需的大小。

Nginx源码阅读:ngx_list_t 链表

四、ngx_init_cycle中初始化共享内存和管理共享内存的数据结构

在ngx_init_cycle中

1、内存池分配(用于组织共享内存的数据结构)

通过内存池,初始化ngx_list_t这个链表,一个chunck划分为n片区域,每片区域大小为一个ngx_shm_zone_t结构体(用于配置共享内存信息的结构体)的大小。

if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))
        != NGX_OK)
    {
        ngx_destroy_pool(pool);
        return NULL;
    }

2、共享内存分配

if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
            goto failed;
        }

3、共享内存初始化的流程

这里主要是遍历链表ngx_list_t,然后根据每个ngx_zone_t创建对应的共享内存

ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
  ...
    part = &cycle->shared_memory.part;//取出共享内存链表中的一个chunck(part)
    shm_zone = part->elts;//取出chunck的共享内存区的首地址(chunck是分为一片片共享内存区)
    for (i = 0; /* void */ ; i++) {
        //这一步是为了初始化下一块chunck
        if (i >= part->nelts) {//一个chunck(part)有nelts个共享内存区
            if (part->next == NULL) {//如果是最后一个chunck,那么退出
                break;
            }
            part = part->next;//继续create下一块chunck(part)
            shm_zone = part->elts;//下一块chunck的共享内存部分头地址
            i = 0;//create下一块chunck,因此i复位为0
        }
        //出错的情况:如果chunck的某片共享内存区大小为0,那么就会失败
        if (shm_zone[i].shm.size == 0) {
            ngx_log_error(NGX_LOG_EMERG, log, 0,
                          "zero size shared memory zone \"%V\"",
                          &shm_zone[i].shm.name);
            goto failed;
        }
        shm_zone[i].shm.log = cycle->log;
        opart = &old_cycle->shared_memory.part;
        oshm_zone = opart->elts;
        //遍历chunck中每片共享内存区(ngx_shm_zone_t)
        for (n = 0; /* void */ ; n++) {
            if (n >= opart->nelts) {
                if (opart->next == NULL) {
                    break;
                }
                opart = opart->next;
                oshm_zone = opart->elts;
                n = 0;
            }
            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
                continue;
            }
            if (ngx_strncmp(shm_zone[i].shm.name.data,
                            oshm_zone[n].shm.name.data,
                            shm_zone[i].shm.name.len)
                != 0)
            {
                continue;
            }
            if (shm_zone[i].tag == oshm_zone[n].tag
                && shm_zone[i].shm.size == oshm_zone[n].shm.size
                && !shm_zone[i].noreuse)
            {
                shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
#if (NGX_WIN32)
                shm_zone[i].shm.handle = oshm_zone[n].shm.handle;
#endif
                //初始化 一片共享内存的结构体数据(chunck中的一部分)
                if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
                    != NGX_OK)
                {
                    goto failed;
                }
                goto shm_zone_found;
            }
            break;
        }
        //初始化共享内存(真正的共享数据在这里,上面的一些结构如ngx_list、shm_zone,都是通过内存池创建的)
        if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
            goto failed;
        }
        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
            goto failed;
        }
        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
            goto failed;
        }
    shm_zone_found:
        continue;
    }
    ...
}

五、使用共享内存

ngx_shared_memory_add返回ngx_shm_zone_t,里面就包含了指向共享内存的指针。

它去使用或者创建一块共享内存,然后交给ngx_list_t去管理,因此这里使用了add

ngx_shm_zone_t *
ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
{
    ngx_uint_t        i;
    ngx_shm_zone_t   *shm_zone;
    ngx_list_part_t  *part;
    part = &cf->cycle->shared_memory.part;//第一块chunck
    shm_zone = part->elts;//指向chunck中数据部分的首地址
    for (i = 0; /* void */ ; i++) {
        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }
            part = part->next;
            shm_zone = part->elts;
            i = 0;
        }
        if (name->len != shm_zone[i].shm.name.len) {
            continue;
        }
        if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)
            != 0)
        {
            continue;
        }
        if (tag != shm_zone[i].tag) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "the shared memory zone \"%V\" is "
                            "already declared for a different use",
                            &shm_zone[i].shm.name);
            return NULL;
        }
        if (shm_zone[i].shm.size == 0) {
            shm_zone[i].shm.size = size;
        }
        if (size && size != shm_zone[i].shm.size) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "the size %uz of shared memory zone \"%V\" "
                            "conflicts with already declared size %uz",
                            size, &shm_zone[i].shm.name, shm_zone[i].shm.size);
            return NULL;
        }
        return &shm_zone[i];
    }
    shm_zone = ngx_list_push(&cf->cycle->shared_memory);//从共享内存中取出一个区域
    if (shm_zone == NULL) {
        return NULL;
    }
    shm_zone->data = NULL;
    shm_zone->shm.log = cf->cycle->log;
    shm_zone->shm.addr = NULL;
    shm_zone->shm.size = size;
    shm_zone->shm.name = *name;
    shm_zone->shm.exists = 0;
    shm_zone->init = NULL;//这块共享内存,可以自己初始化成想要的类型
    shm_zone->tag = tag;
    shm_zone->noreuse = 0;
    return shm_zone;
}
相关文章
|
6月前
|
缓存 监控 Java
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
75 1
|
1月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
47 2
|
30天前
|
存储 缓存 应用服务中间件
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
17 0
|
3月前
|
存储 缓存 编译器
Linux源码阅读笔记06-RCU机制和内存优化屏障
Linux源码阅读笔记06-RCU机制和内存优化屏障
|
6月前
|
存储 NoSQL 算法
Redis源码、面试指南(2)内存编码数据结构(下)
Redis源码、面试指南(2)内存编码数据结构
58 4
|
6月前
|
存储 NoSQL API
Redis源码、面试指南(2)内存编码数据结构(上)
Redis源码、面试指南(2)内存编码数据结构
64 0
|
6月前
|
存储 缓存 Linux
|
6月前
|
存储 C++
C++STL模板之——list(简化源码,模拟源码)
C++STL模板之——list(简化源码,模拟源码)
|
6月前
|
应用服务中间件 nginx C++
nginx内存池源码剖析
小块内存使用尾插法,大块内存使用头插法,大块内存申请挂载时如果前面三个节点都有对象,则不再向后遍历,直接创建一个节点插在头部如果一个小块内存超过四次都没有成功分配出内存,则认为他已经没有足够的内存了,则会抛弃(之后不在考虑在这个节点上分配内存)小块内存节点的内存不回收,但是大内存块的节点可以回收,提供回收方法内存对齐,多处内存对齐减少内存跨 cache 的数量。
68 0
nginx内存池源码剖析
|
11天前
|
应用服务中间件 BI nginx
Nginx的location配置详解
【10月更文挑战第16天】Nginx的location配置详解

热门文章

最新文章