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;
}
相关文章
|
7月前
|
缓存 监控 Java
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
ThreadLocal 源码解析get(),set(), remove()用不好容易内存泄漏
89 1
|
7月前
|
应用服务中间件 Linux 网络安全
CentOS 7.4源码编译nginx1.12 并且隐藏nginx的版本
CentOS 7.4源码编译nginx1.12 并且隐藏nginx的版本
128 0
|
2月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
73 2
|
2月前
|
存储 缓存 应用服务中间件
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
Nginx入门 -- 基本数据结构中之ngx_list_t,ngx_queue_t
37 0
|
3月前
|
负载均衡 网络协议 应用服务中间件
web群集--rocky9.2源码部署nginx1.24的详细过程
Nginx 是一款由 Igor Sysoev 开发的开源高性能 HTTP 服务器和反向代理服务器,自 2004 年发布以来,以其高效、稳定和灵活的特点迅速成为许多网站和应用的首选。本文详细介绍了 Nginx 的核心概念、工作原理及常见使用场景,涵盖高并发处理、反向代理、负载均衡、低内存占用等特点,并提供了安装配置教程,适合开发者参考学习。
|
4月前
|
存储 缓存 编译器
Linux源码阅读笔记06-RCU机制和内存优化屏障
Linux源码阅读笔记06-RCU机制和内存优化屏障
|
4月前
|
应用服务中间件 Linux nginx
在CentOS上使用源码包安装Nginx、以及手动启动Nginx的步骤过程
这篇文章介绍了在CentOS系统上使用Nginx源码包进行安装和配置的详细步骤,包括源码包的获取、解压、配置、编译、安装、启动验证以及注意事项。
426 0
在CentOS上使用源码包安装Nginx、以及手动启动Nginx的步骤过程
|
7月前
|
存储 NoSQL 算法
Redis源码、面试指南(2)内存编码数据结构(下)
Redis源码、面试指南(2)内存编码数据结构
65 4
|
7月前
|
消息中间件 Java 关系型数据库
JAVA云HIS医院管理系统源码、基于Angular+Nginx+ Java+Spring,SpringBoot+ MySQL + MyCat
JAVA云HIS医院管理系统 常规模版包括门诊管理、住院管理、药房管理、药库管理、院长查询、电子处方、物资管理、媒体管理等,为医院管理提供更有力的保障。 HIS系统以财务信息、病人信息和物资信息为主线,通过对信息的收集、存储、传递、统计、分析、综合查询、报表输出和信息共享,及时为医院领导及各部门管理人员提供全面、准确的各种数据。
142 1
|
7月前
|
存储 NoSQL API
Redis源码、面试指南(2)内存编码数据结构(上)
Redis源码、面试指南(2)内存编码数据结构
74 0