一、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_t
的addr
去接受
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
所需的大小。
四、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; }