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

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

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

  • 一、Nginx中共享内存的结构
  • 二、Nginx中实现共享内存的部分
    • 1、共享内存配置信息的结构体`ngx_shm_t`
    • 2、分配共享内存`ngx_shm_alloc`
    • 3、释放共享内存`ngx_shm_free`
  • 三、管理共享内存(通过数据结构将它组织起来)
    • 1、`ngx_shm_zone_t`
    • 2、`ngx_list_t`
  • 四、ngx_init_cycle中初始化共享内存和管理共享内存的数据结构
    • 1、内存池分配(用于组织共享内存的数据结构)
    • 2、共享内存分配
    • 3、共享内存初始化的流程
  • 五、使用共享内存

参考链接

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

Nginx源码阅读:共享内存ngx_shm_t和它的组织方式ngx_shm_zone_t、ngx_list_t
一个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_tsize;//共享内存的大小(字节)    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_tngx_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实现

voidngx_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;}

奇石交易网